From 6e2fce60411b71bf63ea5650780df7febad3f926 Mon Sep 17 00:00:00 2001 From: Xiaohe Yang Date: Thu, 22 Jul 2021 23:22:11 +0800 Subject: [PATCH 0001/1723] hydropper: complete hugepage testcase We will set hugepage count to `target` before launching VM. But When no enough memory left in host, the actual count of hugepages will be smaller that the target. This patch add judgement of actual hugepage count after preparing environment. Signed-off-by: Xiaohe Yang --- .../functional/test_microvm_cmdline.py | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/tests/hydropper/testcases/microvm/functional/test_microvm_cmdline.py b/tests/hydropper/testcases/microvm/functional/test_microvm_cmdline.py index a76a21f1d..be68f2168 100644 --- a/tests/hydropper/testcases/microvm/functional/test_microvm_cmdline.py +++ b/tests/hydropper/testcases/microvm/functional/test_microvm_cmdline.py @@ -21,6 +21,9 @@ import pytest import utils.exception import utils.utils_common from utils.config import CONFIG +from utils.utils_logging import TestLog + +LOG = TestLog.get_global_log() def _get_corefilesize(vm_pid, dump_guestcore): """ @@ -213,7 +216,7 @@ def test_microvm_with_seccomp(microvm, with_seccomp): @pytest.mark.acceptance @pytest.mark.parametrize("mem_size", [2 * 1024 * 1024 * 1024]) -def test_lightvm_mem_hugepage(microvm, mem_size): +def test_microvm_mem_hugepage(microvm, mem_size): """ Test lightvm with hugepage configuration for guest RAM. Test lightvm with hugepages by set memory backend. @@ -252,8 +255,16 @@ def test_lightvm_mem_hugepage(microvm, mem_size): capture_output=True, check=False).stdout.strip() _old_hugepages_free = output.decode('utf-8').lstrip("HugePages_Free:").rstrip("kB").strip() - hugepages_count = int(_old_hugepages_count) + vm_mem_size / (int(hugepage_size) * 1024) + 1 - run("sysctl vm.nr_hugepages=%s" % int(hugepages_count), shell=True, check=False) + target_hugepages_count = int(_old_hugepages_count) + vm_mem_size / (int(hugepage_size) * 1024) + 1 + run("sysctl vm.nr_hugepages=%s" % int(target_hugepages_count), shell=True, check=False) + + # Make sure that setting hugepage count is successful, otherwise recover environment and exit. + output = run("cat /proc/meminfo | grep HugePages_Total", shell=True, + capture_output=True, check=False).stdout.strip() + new_hugepages_count = output.decode('utf-8').lstrip("HugePages_Total:").rstrip("kB").strip() + if int(new_hugepages_count) < target_hugepages_count: + recover_host_hugepages(mount_dir, _old_hugepages_count, is_mounted) + pytest.skip("No enough memory left in host to launch VM with hugepages") return mount_dir, _old_hugepages_count, _old_hugepages_free, is_mounted @@ -270,12 +281,17 @@ def test_lightvm_mem_hugepage(microvm, mem_size): test_vm.basic_config(mem_size=mem_size, mem_path=mount_dir) test_vm.launch() - # check remaining hugepages count on host. + # Check remaining hugepages count on host. output = run("cat /proc/meminfo | grep HugePages_Free", shell=True, capture_output=True, check=False).stdout.strip() remain_free_count = output.decode('utf-8').lstrip("HugePages_Free:").rstrip("kB").strip() - assert int(remain_free_count) >= int(old_huge_free) - test_vm.shutdown() + check_failed = False + if int(remain_free_count) < int(old_huge_free): + check_failed = True - recover_host_hugepages(mount_dir, old_huge_cnt, is_mounted) \ No newline at end of file + test_vm.shutdown() + recover_host_hugepages(mount_dir, old_huge_cnt, is_mounted) + if check_failed: + pytest.xfail(reason="Reduction of hugepages is abnormal: current free cnt %d, old free cnt %d" % + (int(remain_free_count), int(old_huge_free))) -- Gitee From 1c3a7a015dd0ae6d280b92ac52c87843d6954528 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 21 Mar 2022 16:45:30 +0800 Subject: [PATCH 0002/1723] 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 be4d7d2dd01a7ffa756c97a519f99d04f0834f81 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 29 Mar 2022 20:21:40 +0800 Subject: [PATCH 0003/1723] 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 0004/1723] 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 93a75751149e7efc83a182c6f766d6d96a60cef6 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 29 Mar 2022 17:17:02 +0800 Subject: [PATCH 0005/1723] 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 535a6801d47180abe66a6fee365586bbeec8999c Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 29 Mar 2022 20:23:39 +0800 Subject: [PATCH 0006/1723] 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 ce8c6475bb3de02dbf066ce8fd9e22427b067cda Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 26 Feb 2022 09:39:58 +0800 Subject: [PATCH 0007/1723] 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 29e8dfd331dedd28d79eaed3637d399242e7effc Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 21 Mar 2022 17:05:03 +0800 Subject: [PATCH 0008/1723] 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 63d79c939cd6eae58524dcc7a8b36ce996aaa244 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 31 Mar 2022 11:43:23 +0800 Subject: [PATCH 0009/1723] 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 d79907b6d3b11fb0f2a098f082a0cb7f2da4624c Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 2 Apr 2022 14:23:41 +0800 Subject: [PATCH 0010/1723] 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 5e5ec76211c24b04a81ed043842b7279a001d4e9 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 30 Sep 2021 09:21:29 +0800 Subject: [PATCH 0011/1723] 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 4c9f9bee957a5c8d0782232683d28d8f67ca1eb3 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 30 Sep 2021 09:37:06 +0800 Subject: [PATCH 0012/1723] 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 2d6cae5619ee8513808f7f36857c2443e98f7517 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 30 Sep 2021 09:42:11 +0800 Subject: [PATCH 0013/1723] 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 6c9d7b550656637c65e531c8179805afc3d5ae14 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 30 Sep 2021 09:46:45 +0800 Subject: [PATCH 0014/1723] 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 428e8c1a9f0ae4501467b4071d1890de0082a501 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 30 Sep 2021 09:53:47 +0800 Subject: [PATCH 0015/1723] 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 8cb921517fb9cfd44fefb4879f92bd500b019caf Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 30 Sep 2021 10:00:28 +0800 Subject: [PATCH 0016/1723] 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 afa301d53fc762475b2ffe28e895d676adaa1878 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 10 Mar 2022 09:04:50 +0800 Subject: [PATCH 0017/1723] 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 e0d0f46d488582068a7ba10170b2f18cb7ccda55 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Sat, 26 Mar 2022 16:27:38 +0800 Subject: [PATCH 0018/1723] 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 3136607d1b31e78da42c7b396f611fae137b2974 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 7 Apr 2022 18:56:17 +0800 Subject: [PATCH 0019/1723] 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 111fbd32e0e1f0e923941f184f1425b37b9e99eb Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Wed, 30 Mar 2022 09:28:24 +0800 Subject: [PATCH 0020/1723] 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 66fa0ae89..6d2c04f2c 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, @@ -146,6 +146,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 223e171c5..a5a4be983 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, @@ -153,6 +153,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 b4a795488449326e60e2830f6e4d883ff9f1d6b6 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Wed, 30 Mar 2022 09:34:54 +0800 Subject: [PATCH 0021/1723] 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 e1d5c1f96dc581eb410e515e30ff77c303912955 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 7 Apr 2022 20:11:38 +0800 Subject: [PATCH 0022/1723] 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 6d2c04f2c..ef3a9bf48 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 a5a4be983..70a2e1ec2 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 fe785073dabe015737f7b0046df9776598ffbbe4 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 6 Jan 2022 17:32:26 +0800 Subject: [PATCH 0023/1723] 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 7ae9aac49..ff586eaf2 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 3a79a957ab31790f2ec7d8d62deabdafc56baf28 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 6 Jan 2022 17:35:45 +0800 Subject: [PATCH 0024/1723] 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 c10a4ae3d70ebf1a265c5783b884d208c320ed09 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Fri, 7 Jan 2022 11:40:20 +0800 Subject: [PATCH 0025/1723] 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 1c9820457..897147930 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -677,6 +677,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 14bc8fd99bddb0815804dae1cc9702c0b6ecf4b5 Mon Sep 17 00:00:00 2001 From: ace-yan Date: Sun, 17 Apr 2022 15:49:41 +0800 Subject: [PATCH 0026/1723] 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 092b04eb6aea8e0ab63256b265a9bddaed2264cf Mon Sep 17 00:00:00 2001 From: Zuo Xiaoxian Date: Wed, 20 Apr 2022 15:56:51 +0800 Subject: [PATCH 0027/1723] 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 beb454ba1a4e3d30e26d38a2e0334a1188770f05 Mon Sep 17 00:00:00 2001 From: Ming Yang Date: Sat, 23 Apr 2022 16:26:00 +0800 Subject: [PATCH 0028/1723] 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 6bfb34bf1f5d09325074bdc650d538c4499f959b Mon Sep 17 00:00:00 2001 From: Ming Yang Date: Sat, 23 Apr 2022 16:50:20 +0800 Subject: [PATCH 0029/1723] 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 a03b3a0c10abd50ed336f9e2bb79c49c51053f0f Mon Sep 17 00:00:00 2001 From: Ming Yang Date: Sat, 23 Apr 2022 17:06:52 +0800 Subject: [PATCH 0030/1723] 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 a40504471b96d0ba5fa70fcf957c6947847c732b Mon Sep 17 00:00:00 2001 From: Zuo xiaoxian Date: Sun, 24 Apr 2022 19:57:43 +0800 Subject: [PATCH 0031/1723] 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 12874b591df09934fb1f075eaf2db0f03619b363 Mon Sep 17 00:00:00 2001 From: Zuo xiaoxian Date: Sun, 24 Apr 2022 20:14:30 +0800 Subject: [PATCH 0032/1723] 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 8090632014b1c4bc46f5a0e7396e8f1f17275500 Mon Sep 17 00:00:00 2001 From: Ming Yang Date: Wed, 27 Apr 2022 21:21:07 +0800 Subject: [PATCH 0033/1723] 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 0fab5525c28d652eff809f0d82af369f6510b25f Mon Sep 17 00:00:00 2001 From: Ming Yang Date: Wed, 27 Apr 2022 22:31:59 +0800 Subject: [PATCH 0034/1723] 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 d1138b074d2f34ac2e07c5175c49fdd741b30b08 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Fri, 8 Apr 2022 15:22:20 +0800 Subject: [PATCH 0035/1723] 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 d68875c4916c8ed375ff4547af2536bfb1d882ea Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Sat, 23 Apr 2022 14:49:15 +0800 Subject: [PATCH 0036/1723] 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 3c90da04590de6642791e141a0326061978fc257 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 12 Apr 2022 21:12:45 +0800 Subject: [PATCH 0037/1723] 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 b780082f6ee0f55822409c8bea3d99fd0b09b0f4 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 12 Apr 2022 21:14:12 +0800 Subject: [PATCH 0038/1723] 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 9504c86fe49f13bcf3a92c4d673932c4d78cbde4 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 1 Mar 2022 12:41:35 +0800 Subject: [PATCH 0039/1723] 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 ede8b5aed0b934da184fd76c7286d7bb5bc7acb5 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Fri, 8 Apr 2022 14:32:03 +0800 Subject: [PATCH 0040/1723] 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 564bcdec5eaa19c59e08ceefe6d0f5075b534f78 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 12 Apr 2022 16:54:57 +0800 Subject: [PATCH 0041/1723] 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 76f1d18e981472c13078d1cfa4f1ffc774e72cef Mon Sep 17 00:00:00 2001 From: ace-yan Date: Mon, 16 May 2022 22:21:51 +0800 Subject: [PATCH 0042/1723] 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 f860b6f89c0b82e09ac1b501f6312f5b42339854 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Tue, 8 Feb 2022 10:07:53 +0800 Subject: [PATCH 0043/1723] 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 15d2e0babf273f708930fc33590e6eadec68107f Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 20 Jan 2022 19:14:38 +0800 Subject: [PATCH 0044/1723] 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 83ab632cd99d440e6a6187c2784ac0713250b764 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Fri, 21 Jan 2022 09:18:13 +0800 Subject: [PATCH 0045/1723] 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 0cd71132450423d8f8daa7f46a03b2a05afa89ab Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 20 Jan 2022 19:20:31 +0800 Subject: [PATCH 0046/1723] 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 85ee86bab9f724aef3a0a0a133e167b019cf8cc8 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Tue, 8 Feb 2022 10:37:39 +0800 Subject: [PATCH 0047/1723] 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 542119558104227c35a98699b5e7a757d1e7d137 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 20 Jan 2022 20:03:40 +0800 Subject: [PATCH 0048/1723] 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 b44cad2dfde29c8e48bd3c04350b271704467dab Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Tue, 8 Feb 2022 10:43:20 +0800 Subject: [PATCH 0049/1723] 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 d1b6e843ebeeddf53db92fa35888c0294e1885c1 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 20 Jan 2022 20:25:03 +0800 Subject: [PATCH 0050/1723] 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 cc5e38eedac5b1c59b5d456c5ed51a6b12eac655 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 20 Jan 2022 20:29:17 +0800 Subject: [PATCH 0051/1723] 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 1164bdc38802c477570326c968b30bf2c8d74a55 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Fri, 20 May 2022 14:35:29 +0800 Subject: [PATCH 0052/1723] 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 97a19f45f0ef90fd288cb3cd915d9a1842319bec Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Mon, 23 May 2022 17:09:15 +0800 Subject: [PATCH 0053/1723] 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 7bfd5bde42cf1a14054f6f87383243d0f9f901e8 Mon Sep 17 00:00:00 2001 From: linmaolin Date: Wed, 13 Apr 2022 21:36:24 +0800 Subject: [PATCH 0054/1723] 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 1c51beaffab782b8ab771f4f2dae276b540d6f3b Mon Sep 17 00:00:00 2001 From: linmaolin Date: Wed, 25 May 2022 17:50:20 +0800 Subject: [PATCH 0055/1723] 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 fc4a5a11cd99ca66320a4a12588ae60becc8d2d1 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Wed, 25 May 2022 14:41:01 +0800 Subject: [PATCH 0056/1723] 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 abb9a8d460c60fbdfe104dfc5e9e44b902bcb22a Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 24 May 2022 11:25:13 +0800 Subject: [PATCH 0057/1723] 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 74048ef11fa32e7f5ccbf8df5bfd05e1685faf00 Mon Sep 17 00:00:00 2001 From: ace-yan Date: Thu, 5 May 2022 22:07:48 +0800 Subject: [PATCH 0058/1723] 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 0589587e9f1d256871462dc5e52a93de1d83fdf3 Mon Sep 17 00:00:00 2001 From: ace-yan Date: Fri, 3 Jun 2022 22:46:48 +0800 Subject: [PATCH 0059/1723] 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 7a98aaab1c00d9ca167db1dc035c9345d8671c7c Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Tue, 7 Jun 2022 11:54:47 +0800 Subject: [PATCH 0060/1723] 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 7ee976902184c3deb73a9e36abcdfdfa96dfbc29 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Tue, 31 May 2022 20:25:19 +0800 Subject: [PATCH 0061/1723] 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 c1d490a62a66fde8fb6a2cf7348cad9d6a65732a Mon Sep 17 00:00:00 2001 From: uran0sH Date: Mon, 13 Jun 2022 19:48:38 +0800 Subject: [PATCH 0062/1723] 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 | 137 +++++++++++++------ 10 files changed, 322 insertions(+), 84 deletions(-) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index de6163e30..a85ca8231 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -64,6 +64,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)] @@ -163,6 +181,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 37a4ee7da..be310f7ec 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -95,12 +95,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; @@ -151,7 +155,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. /// @@ -283,7 +287,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( @@ -296,6 +300,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(()) } @@ -693,6 +704,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. @@ -706,19 +719,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)), } } @@ -745,11 +765,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) } } @@ -922,6 +940,7 @@ mod tests { let microvm_cpu_topo = CpuTopology { sockets: test_nr_cpus, + dies: 1, cores: 1, threads: 1, nrcpus: test_nr_cpus, @@ -934,10 +953,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 f376a0835..173e566e9 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -51,6 +51,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)] @@ -74,6 +79,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)] @@ -81,6 +107,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, @@ -114,6 +144,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() } } @@ -155,6 +189,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 @@ -346,6 +392,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"), @@ -408,20 +457,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 5aa88e52c..999e43e61 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 46a966c5a..eaf5b00b3 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -114,7 +114,7 @@ use address_space::KvmIoListener; use address_space::{ create_host_mmaps, set_host_memory_policy, 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 55e0ce126..d655af024 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -68,7 +68,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")] @@ -230,7 +230,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, @@ -760,9 +767,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 8aa77e3b2..733cbb4b5 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -30,7 +30,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, }; @@ -142,7 +142,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( @@ -467,6 +474,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 42f19b989..61dc7dbcc 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::{ errors::ErrorKind as DevErrorKind, FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, SERIAL_ADDR, @@ -126,7 +126,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())) @@ -476,9 +483,15 @@ impl MachineOps for StdMachine { } 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_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 62435324f..7e61a40b8 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 42c44d9e2..708797090 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -19,6 +19,11 @@ use super::errors::{ErrorKind, Result, ResultExt}; use crate::config::{CmdParser, ConfigCheck, ExBool, VmConfig, MAX_NODES, MAX_STRING_LENGTH}; 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; @@ -121,6 +126,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, } @@ -130,6 +140,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(), } } @@ -229,7 +244,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"); @@ -239,26 +256,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) { @@ -272,8 +291,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(()) } @@ -348,12 +382,50 @@ 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) +} + /// 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')) { @@ -415,7 +487,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()); @@ -732,30 +809,6 @@ 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()); } #[test] -- Gitee From dbdadf028a803dd91d4dc5e4467423db06f2ee54 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 16 Jun 2022 10:08:18 +0800 Subject: [PATCH 0063/1723] 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 999e43e61..68b18ad7b 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -255,7 +255,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 22c0f76e84352bab0af00c90ef4c3ea9dcb89fc1 Mon Sep 17 00:00:00 2001 From: uran0sH Date: Thu, 16 Jun 2022 20:45:33 +0800 Subject: [PATCH 0064/1723] 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 68b18ad7b..d00a6d7ff 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 733cbb4b5..cd6af51a8 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, ); @@ -833,6 +835,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 { @@ -1157,56 +1204,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 25cc3a356..3b99327c2 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 61dc7dbcc..4c5dc3925 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 708797090..277cb4007 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 cfd9125d3088a02efc799c1c38dcace4a42e4bc1 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 20 Jun 2022 19:10:13 +0800 Subject: [PATCH 0065/1723] 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 3aa81e516be900aedf022828b1e009710099a7ed Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 20 Jun 2022 19:21:25 +0800 Subject: [PATCH 0066/1723] 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 c31958002a768be815f6aa239d15ecc0af84abbe Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Wed, 22 Jun 2022 21:26:37 +0800 Subject: [PATCH 0067/1723] 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 64f82e53403fb6d1b33b735186c4f8925eb4e6d9 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 20 Jun 2022 19:52:40 +0800 Subject: [PATCH 0068/1723] 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 8a884483eedd10ec99d59dc17fd1b9e6ac399b7d Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 20 Jun 2022 20:02:35 +0800 Subject: [PATCH 0069/1723] 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 733463abb415500166d922db0a03dd6483fd781c Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 20 Jun 2022 19:32:11 +0800 Subject: [PATCH 0070/1723] 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 277cb4007..337555190 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 abf6ef95b5dd6b78e7cafbe055767bfbc0c7e6b2 Mon Sep 17 00:00:00 2001 From: louquuan18 <2710830093@qq.com> Date: Thu, 30 Jun 2022 15:07:18 +0800 Subject: [PATCH 0071/1723] 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 b637887172c17659b7b498578856e867033345b7 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 12 Jul 2022 20:53:15 +0800 Subject: [PATCH 0072/1723] 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 c994804d0f34aaf9770b2bc3b4031f06130d963c Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 5 Jul 2022 14:26:46 +0800 Subject: [PATCH 0073/1723] 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 821af8023ca70fed6fd1e8e9fb2997d1d3ec5ed6 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Wed, 13 Jul 2022 18:22:55 +0800 Subject: [PATCH 0074/1723] 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 b79e8cc823028cb5ef1e0bbe3a1fcd3cb58d1a88 Mon Sep 17 00:00:00 2001 From: uran0sH Date: Mon, 4 Jul 2022 10:52:40 +0800 Subject: [PATCH 0075/1723] 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 28b647144..0e9e99200 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -1428,7 +1428,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 337555190..f122748dd 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 d67925744015ec9c1be512fdc6f731a0269bbf67 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 14 Jul 2022 16:31:14 +0800 Subject: [PATCH 0076/1723] 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 db8fda0fd84d2a005adfd475aa90d96265e3a4fd Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 12 Jul 2022 15:28:09 +0800 Subject: [PATCH 0077/1723] 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 6af47e0f8..6baf28a53 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 20b27ee2c6796535b3cb59a98dfa68c7c467c000 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 18 Jul 2022 21:20:03 +0800 Subject: [PATCH 0078/1723] 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 6baf28a53..63243624d 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 faced857d1c3ecb9c7401327bca837cfc7db0005 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 18 Jul 2022 20:56:45 +0800 Subject: [PATCH 0079/1723] 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 3eb4ff7801d42446069c74e17663b0d0cd9aa237 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 25 Jul 2022 21:14:16 +0800 Subject: [PATCH 0080/1723] 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 34106dc104d4128dba6d1e2442b2dcf10a49d6f3 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sat, 30 Jul 2022 20:39:52 +0800 Subject: [PATCH 0081/1723] 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 2634f0f60ca2ab319d06f074370c1bf0bd04d400 Mon Sep 17 00:00:00 2001 From: ace-yan Date: Fri, 24 Jun 2022 21:36:53 +0800 Subject: [PATCH 0082/1723] 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 65adc30ff..6208c771e 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 c514eead58efea27b9232967d537f2b73f856d94 Mon Sep 17 00:00:00 2001 From: Zuo Xiaoxian Date: Thu, 30 Jun 2022 14:05:40 +0800 Subject: [PATCH 0083/1723] 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 c38fae0b09e7bff18e93c120e2b284c22e83506b Mon Sep 17 00:00:00 2001 From: Zhang Yang Date: Mon, 8 Aug 2022 16:21:29 +0800 Subject: [PATCH 0084/1723] 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 f122748dd..ccb4c6d0c 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 e9c15fdc9fc0ce33c428561e68dc01f440feda9e Mon Sep 17 00:00:00 2001 From: Zhang Yang Date: Wed, 13 Jul 2022 18:10:15 +0800 Subject: [PATCH 0085/1723] 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 cb2e58a3e..2240edec8 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -498,9 +498,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. @@ -509,9 +510,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 6b66a9157dba2766f0a11a2f291137e1775403fa Mon Sep 17 00:00:00 2001 From: Zhang Yang Date: Wed, 13 Jul 2022 18:11:09 +0800 Subject: [PATCH 0086/1723] 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 3e43158bd7544247b36f78eb5ef6a92db013d14d Mon Sep 17 00:00:00 2001 From: Zhang Yang Date: Tue, 19 Jul 2022 11:01:30 +0800 Subject: [PATCH 0087/1723] 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 3e3fd00c7e7c95f18f0e81b298835124064fa4db Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 21 Jul 2022 12:17:20 +0800 Subject: [PATCH 0088/1723] 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 ae6fe469e03194d820df81bb88fa53f2e1095ef0 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 21 Jul 2022 15:18:59 +0800 Subject: [PATCH 0089/1723] 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 63355006e311d3b29e5a2efb032e28e27fc9d3ee Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Wed, 3 Aug 2022 15:37:20 +0800 Subject: [PATCH 0090/1723] 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 60554e380..7f46e6db3 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 81670342e08c707bea8c6fd169cac24e4fbef96c Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 21 Jul 2022 15:31:39 +0800 Subject: [PATCH 0091/1723] 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 385e1a0e0b5f67ee8d22a5f3540e28bdd4784f24 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 21 Jul 2022 15:55:41 +0800 Subject: [PATCH 0092/1723] 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 6208c771e..dcbbb73f7 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; @@ -275,6 +278,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" })?; @@ -1025,10 +1031,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 b1098af63d293cf2ec714066b32cccc809828e0a Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 21 Jul 2022 14:10:38 +0800 Subject: [PATCH 0093/1723] 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 4a525a59a90028cbd99d5257a93e2419771c7b58 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Sat, 23 Jul 2022 22:55:25 +0800 Subject: [PATCH 0094/1723] 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 c8e0eefb8150ed9c08810301b808b988d3fe3671 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 21 Jul 2022 14:21:38 +0800 Subject: [PATCH 0095/1723] 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 7c0bb781d..a5061d2fa 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 pub enum LayoutEntryType { @@ -125,6 +126,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, @@ -312,10 +314,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 } @@ -416,7 +414,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; @@ -430,7 +428,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, )?; @@ -459,7 +456,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 @@ -494,7 +492,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")?; @@ -502,6 +500,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); } @@ -549,6 +549,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) } @@ -950,24 +962,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)), @@ -975,17 +973,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 e5302ee03..6b037dded 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; @@ -112,6 +112,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, @@ -335,10 +336,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 } @@ -441,11 +438,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(); @@ -456,7 +449,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, )?; @@ -478,7 +470,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 @@ -496,7 +489,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")?; @@ -509,6 +502,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); } @@ -590,6 +589,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) } @@ -902,24 +913,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)), @@ -927,17 +924,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 e4761fabe2f0d36f72bb0bef5e16fa912673c05a Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 21 Jul 2022 14:22:13 +0800 Subject: [PATCH 0096/1723] 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 a5061d2fa..420f6af17 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::{ @@ -456,8 +456,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 @@ -492,7 +492,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")?; @@ -553,12 +553,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>> { @@ -966,12 +966,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 6b037dded..fd322dc45 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::{ @@ -470,8 +470,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 @@ -489,7 +489,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")?; @@ -593,12 +593,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>> { @@ -917,12 +917,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 ed935ea53..7ba9dc115 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 a6b7c92da154f04ec825981109fc3ed7e94bcda6 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 1 Aug 2022 10:39:09 +0800 Subject: [PATCH 0097/1723] 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 2240edec8..2975fc15d 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -644,14 +644,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 b435d47860dfdd7825e68a3ef091526c6aa3bbc1 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 25 Jul 2022 09:02:14 +0800 Subject: [PATCH 0098/1723] 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 7ba9dc115..73d32569f 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 2a5f12606b98e5182103c3bb87a56ce9bed9413a Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 6 Aug 2022 14:57:34 +0800 Subject: [PATCH 0099/1723] 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 60bbb4a4c0ec795b22cb4461fdcd4b469e416cdd Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 11 Aug 2022 20:44:55 +0800 Subject: [PATCH 0100/1723] 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 9d68798e187960e29a2d941d710e9691ec19a470 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Sat, 13 Aug 2022 14:17:02 +0800 Subject: [PATCH 0101/1723] 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 fe32d44e6324bd30cddfff5931f81b70fd75c03d Mon Sep 17 00:00:00 2001 From: ace-yan Date: Fri, 12 Aug 2022 07:12:44 +0800 Subject: [PATCH 0102/1723] 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 e6d1b434c..7011ce7ab 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 fb38461e5..6bd2d46fd 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 314488f24..c11f3c91d 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")] @@ -1308,12 +1308,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 420f6af17..0b7bf6de5 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -440,7 +440,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 fd322dc45..a6c368dfa 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, @@ -458,7 +458,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 195166f39..2c22fe71f 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; @@ -583,8 +582,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 946e25705725ae99e3771482d140ac2c68a6b95a Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Sun, 14 Aug 2022 06:43:20 +0000 Subject: [PATCH 0103/1723] 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 20dbc9ea4..83b8758f1 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -445,8 +445,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 c970de79ace205deba5f3827932ee505b05b7a07 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Sat, 13 Aug 2022 16:30:40 +0800 Subject: [PATCH 0104/1723] 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 ad7b14d1df19fee5ee4a32a549c1bb38b81d1cd8 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Sat, 13 Aug 2022 16:44:04 +0800 Subject: [PATCH 0105/1723] 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 22dff21c9e6b8ea4d5e0e4deae5c39f1c8fb8d94 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 12 Aug 2022 11:24:27 +0800 Subject: [PATCH 0106/1723] 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 2856f8ad3018bf4db7f834f787d100b63eb64c25 Mon Sep 17 00:00:00 2001 From: ace-yan Date: Fri, 19 Aug 2022 22:14:06 +0800 Subject: [PATCH 0107/1723] 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 e5a4c70460beb41904d06a10ec2f4ac3bec7ab45 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 12 Aug 2022 14:21:31 +0800 Subject: [PATCH 0108/1723] 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 6a34f6615b7a15cb8c532b327d23dfa11425c75f Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 12 Aug 2022 15:01:17 +0800 Subject: [PATCH 0109/1723] 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 5700bb27df8c8793302d03c65ae729dd0f766d03 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 12 Aug 2022 18:27:54 +0800 Subject: [PATCH 0110/1723] 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 ac7c9e8371c86a1c306f1c855217a9af98259a35 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 12 Aug 2022 21:23:55 +0800 Subject: [PATCH 0111/1723] 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 61fe7d18e0339508a71a165e55838872e3e858f6 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 12 Aug 2022 21:28:20 +0800 Subject: [PATCH 0112/1723] 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 69cd08996354d02cfae28bc4b76ccc140e827fd5 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 12 Aug 2022 21:43:57 +0800 Subject: [PATCH 0113/1723] 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 26d7112dadd3b75d76312d6fdf855599c0498cf6 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sun, 14 Aug 2022 22:22:04 +0800 Subject: [PATCH 0114/1723] 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 7593ca0f608c9349fdfaaefcf2cdc126f8968024 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sun, 14 Aug 2022 22:27:02 +0800 Subject: [PATCH 0115/1723] 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 c78b7c04509fcf454d5ab5d418891337b5776ee7 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 12 Aug 2022 21:49:49 +0800 Subject: [PATCH 0116/1723] 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 02ce14e692ab6cfa428df055e881891caf34178b Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 12 Aug 2022 22:03:07 +0800 Subject: [PATCH 0117/1723] 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 8a7dd84b4..37014971d 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -625,6 +625,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 1cf7256b7c22a5f49b5cd3c908d8443a4cd0dcf6 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 18 Aug 2022 21:07:35 +0800 Subject: [PATCH 0118/1723] 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 e601c2f2a506002bd52838db696edd85c722265c Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 18 Aug 2022 21:23:16 +0800 Subject: [PATCH 0119/1723] 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 37014971d..e88186791 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -640,6 +640,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 f021c50658a42be7da5eb0e5e20bfa316f2aae44 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 18 Aug 2022 21:26:48 +0800 Subject: [PATCH 0120/1723] 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 e88186791..c1c164464 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -652,7 +652,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 a26a92e6bb027c03b1fcbc8db85ef3166115e889 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 22 Aug 2022 17:35:14 +0800 Subject: [PATCH 0121/1723] 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 e99b8696a24f0e2d8668cdbeb53207cb8a269528 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 22 Aug 2022 17:49:06 +0800 Subject: [PATCH 0122/1723] 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 106f9ddb6ba64797bdb8e22c82438fb2d133d493 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 22 Aug 2022 17:54:17 +0800 Subject: [PATCH 0123/1723] 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 f412cc3b3bd3d63076df69a03e31398c5d6cc721 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 22 Aug 2022 18:14:37 +0800 Subject: [PATCH 0124/1723] 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 3598540ff4229cec960cec8d20913936737d201e Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 22 Aug 2022 18:24:24 +0800 Subject: [PATCH 0125/1723] 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 9df3460db2da748f37df5e741277dcf1ce5f03a0 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 22 Aug 2022 19:03:25 +0800 Subject: [PATCH 0126/1723] 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 676871a30865c139aa0614ce154de74bba7dcae9 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 22 Aug 2022 19:39:52 +0800 Subject: [PATCH 0127/1723] 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 35b894314732746e3562c41a3e7b89bba118f53e Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 22 Aug 2022 20:04:24 +0800 Subject: [PATCH 0128/1723] 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 5af5da088591a4d7823cb90de87639c0bc6f88ef Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Sun, 21 Aug 2022 01:48:35 +0800 Subject: [PATCH 0129/1723] 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 8a72582d384db42cb08cd8a591c0e27fa11b6bd0 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Sun, 21 Aug 2022 05:06:55 +0800 Subject: [PATCH 0130/1723] 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 ccb4c6d0c..5a9c015e9 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 18c3e55458feb2bb9da347a93b1fcf8e5f4fa458 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Sun, 21 Aug 2022 06:02:44 +0800 Subject: [PATCH 0131/1723] 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 686571404994a4bc47b2db96d41909d56d114419 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Sun, 21 Aug 2022 10:08:34 +0800 Subject: [PATCH 0132/1723] 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 29b84eb5a04bc234687601002b4fded50eea95e8 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Sun, 21 Aug 2022 11:16:21 +0800 Subject: [PATCH 0133/1723] 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 533e4a1f5bf7b5c2ec17f5830786413e0c2c4906 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Mon, 22 Aug 2022 00:03:58 +0800 Subject: [PATCH 0134/1723] 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 92da85b5ffed70d49a1f1a4606009b5673d80f81 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Mon, 22 Aug 2022 00:50:48 +0800 Subject: [PATCH 0135/1723] 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 bf83ef37b94c813cb0e5d5b2244358c29a3a382b Mon Sep 17 00:00:00 2001 From: Zhang Yang Date: Thu, 18 Aug 2022 10:45:36 +0800 Subject: [PATCH 0136/1723] 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 6cb635dbf5252fb4e794cdae3b2aab0364ca8bae Mon Sep 17 00:00:00 2001 From: Zhang Yang Date: Fri, 19 Aug 2022 10:47:45 +0800 Subject: [PATCH 0137/1723] 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 c1c164464..cdef72f03 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -517,6 +517,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 3d0e262612711109de2eb6642b2157d5af203636 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 24 Aug 2022 12:54:32 +0800 Subject: [PATCH 0138/1723] 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/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 +----- 12 files changed, 16 insertions(+), 39 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/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 8316f6ddd1d5e992041e60e0491ee73efec89e2d Mon Sep 17 00:00:00 2001 From: Zhao Mengmeng Date: Wed, 24 Aug 2022 16:55:07 +0800 Subject: [PATCH 0139/1723] 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 31dddf379a3afe0f2f6e346b2bccc614c67453e3 Mon Sep 17 00:00:00 2001 From: YeXiao Date: Wed, 24 Aug 2022 10:01:00 +0800 Subject: [PATCH 0140/1723] 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 88b6cca43..664622a1f 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 0abdfbf675450454267541cea7c3991775338aef Mon Sep 17 00:00:00 2001 From: XiaoyangXu Date: Wed, 24 Aug 2022 14:32:26 +0800 Subject: [PATCH 0141/1723] 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 42c05b35828d8b6b060dc2b405516f1d356d0559 Mon Sep 17 00:00:00 2001 From: uran0sH Date: Fri, 19 Aug 2022 14:47:03 +0800 Subject: [PATCH 0142/1723] 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 613d8f704..c96083736 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 b87dfbdc5ce1cb6649ddcb273a1ac9d1e0c5b077 Mon Sep 17 00:00:00 2001 From: uran0sH Date: Thu, 21 Jul 2022 11:00:51 +0800 Subject: [PATCH 0143/1723] 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 86e1d3d74..7a215b595 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")] @@ -786,11 +787,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), + } } } @@ -971,10 +988,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 { @@ -988,10 +1005,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 { @@ -1005,10 +1022,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); @@ -1023,14 +1040,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 c11f3c91d..961ca9bf2 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -954,13 +954,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[") @@ -1001,13 +995,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, @@ -1016,13 +1004,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 4eb8f1f45..dfda8b843 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -960,13 +960,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 c712c5e40..7f3284329 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -868,6 +868,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 e012bf99fe2bb6644fdb73289da9bbeede579b66 Mon Sep 17 00:00:00 2001 From: Wu Binfeng Date: Mon, 11 Apr 2022 15:46:00 +0800 Subject: [PATCH 0144/1723] 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 1d8ea5ca4..cc3434937 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -676,10 +676,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( @@ -698,7 +704,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 dfda8b843..df3f1f236 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 7f3284329..2029f8eb8 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 c51ae5b37fb595f4b9d16621300f164d3bd742c1 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 18 Aug 2022 17:19:01 +0800 Subject: [PATCH 0145/1723] 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 cc3434937..8831edc52 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; @@ -962,7 +963,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"); @@ -970,6 +971,8 @@ pub trait MachineOps { } else { bail!("No bus device found"); } + let mut locked_input = INPUT.lock().unwrap(); + locked_input.keyboard = Some(kbd); Ok(()) } @@ -989,7 +992,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"); @@ -997,6 +1000,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 664622a1f..f24b21a27 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 e96e1345ff8762e0031c286a451bfa2ef3b01683 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 1 Sep 2022 20:39:48 +0800 Subject: [PATCH 0146/1723] 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 82bb25028d223300e8ba824ed1c234f0b186d903 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 1 Sep 2022 20:44:13 +0800 Subject: [PATCH 0147/1723] 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 84f250f0a2db075391cfc35d47e4828b1d4a3d65 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 5 Sep 2022 16:10:55 +0800 Subject: [PATCH 0148/1723] 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 8831edc52..057dc5d10 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, }; @@ -716,6 +719,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)?; @@ -1068,9 +1072,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)?; } @@ -1080,6 +1081,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 08d9c8eb38af407bd3b0c50c5941b1e2dd19df71 Mon Sep 17 00:00:00 2001 From: Zhao Mengmeng Date: Wed, 31 Aug 2022 09:46:34 +0800 Subject: [PATCH 0149/1723] 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 d2678ad475a3f6adfe1a0031ca3caec2f90956d0 Mon Sep 17 00:00:00 2001 From: Zhao Mengmeng Date: Wed, 31 Aug 2022 09:54:30 +0800 Subject: [PATCH 0150/1723] 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 7a215b595..9e9a07014 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -718,18 +718,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. @@ -738,16 +738,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]; @@ -755,12 +758,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 961ca9bf2..0c3c1fadc 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 0d86c2b4c..6460524d5 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -152,11 +152,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 5c411ad56..c8f85ab80 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -134,11 +134,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 270b4e87822f5a831915d037b2696ed9050a0436 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 15 Sep 2022 19:14:51 +0800 Subject: [PATCH 0151/1723] 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 f512ac9101503c002e71e76bbc10ff71f95e4f4d Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 15 Sep 2022 19:22:05 +0800 Subject: [PATCH 0152/1723] 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 dc7bc201801ddb36854281950ff27c5917b3a836 Mon Sep 17 00:00:00 2001 From: Zhao Mengmeng Date: Tue, 13 Sep 2022 15:18:51 +0800 Subject: [PATCH 0153/1723] 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 a4392d59379e8d67aeca5fa8cc6899a39f23fcd8 Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Wed, 14 Sep 2022 02:01:24 +0000 Subject: [PATCH 0154/1723] 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 e4b29c7207d77ecd1d889242d9563e428f4426a7 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 14 Sep 2022 10:05:05 +0800 Subject: [PATCH 0155/1723] 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 7a0e8132d7635da08337b1e54b8c2f4cc507e7d0 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 14 Sep 2022 10:17:04 +0800 Subject: [PATCH 0156/1723] 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 bd91f8c282188e406fb363e3e30bc0f95466986a Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 14 Sep 2022 10:42:27 +0800 Subject: [PATCH 0157/1723] 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 fa79e513079568b59b63c203b4e2898643e78b6c Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 14 Sep 2022 10:54:24 +0800 Subject: [PATCH 0158/1723] 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 fdf45ce4ecd879df6648cabb87fe09d33f79be94 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 19 Sep 2022 19:42:51 +0800 Subject: [PATCH 0159/1723] 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 3cc84c6b603c81d611a475cf58debd756128be7c Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 20 Sep 2022 16:53:36 +0800 Subject: [PATCH 0160/1723] 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 a2e38b87be2ddc9013cd5fdf9846de87b8e2e409 Mon Sep 17 00:00:00 2001 From: xuebling Date: Thu, 15 Sep 2022 22:17:07 +0800 Subject: [PATCH 0161/1723] 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 d22bb2ead..edfffe1c7 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 d4cdc7198fe387614a4a90ca5c7bca92dbf6b709 Mon Sep 17 00:00:00 2001 From: xuebling Date: Thu, 15 Sep 2022 22:19:54 +0800 Subject: [PATCH 0162/1723] 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 54d6823bf54f9b0020ad12e924c458cd7d988cd2 Mon Sep 17 00:00:00 2001 From: xuebling Date: Thu, 15 Sep 2022 22:24:45 +0800 Subject: [PATCH 0163/1723] 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 0c3c1fadc..39afe2f70 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 ac77c9594ec198f6fc66a08c640fefc7c235281d Mon Sep 17 00:00:00 2001 From: xuebling Date: Thu, 15 Sep 2022 22:29:27 +0800 Subject: [PATCH 0164/1723] 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 35c204b52150a0541c0de4f0a870936e1b2cbf03 Mon Sep 17 00:00:00 2001 From: xuebling Date: Thu, 15 Sep 2022 22:29:42 +0800 Subject: [PATCH 0165/1723] 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 2b9350b09..d34a292c5 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 36e2d14e4da0ce0a2ca3e077233e65fa716d4bbf Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 20 Sep 2022 16:50:56 +0800 Subject: [PATCH 0166/1723] 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 e3d73e4e2f86ec234ae72ab6dd2ea7225ee8652c Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 20 Sep 2022 17:06:20 +0800 Subject: [PATCH 0167/1723] 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 62d74d0998b05dfd7eb2137cbf2f246b2d333e30 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 20 Sep 2022 17:23:44 +0800 Subject: [PATCH 0168/1723] 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 c1bfcc7563e248d6f8169ee8131e648e4d9372a4 Mon Sep 17 00:00:00 2001 From: Zhao Mengmeng Date: Fri, 16 Sep 2022 16:45:19 +0800 Subject: [PATCH 0169/1723] 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 b6f532d0f..d47739a30 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) } @@ -446,6 +452,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; @@ -646,6 +656,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 ddb9f5ecc068d5e9b2182dce9cebc72e86c5c336 Mon Sep 17 00:00:00 2001 From: louquan18 <2710830093@qq.com> Date: Sat, 10 Sep 2022 11:48:06 +0800 Subject: [PATCH 0170/1723] 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 edfffe1c7..a8fd5b248 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; @@ -1174,6 +1175,7 @@ pub trait MachineOps { EventSet::IN, vec![power_button_handler], ); + trace_eventnotifier(¬ifier); EventLoop::update_event(vec![notifier], None).chain_err(|| ErrorKind::RegNotifierErr)?; Ok(()) @@ -1425,3 +1427,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 39afe2f70..226a4874b 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 { @@ -1584,3 +1607,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 02b69068a..4b25e3c2b 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 8d0ae82c14e75e9601ce17b76bcd0b5044615d29 Mon Sep 17 00:00:00 2001 From: louquan18 <2710830093@qq.com> Date: Sat, 10 Sep 2022 12:01:48 +0800 Subject: [PATCH 0171/1723] 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 ef0dd5f4c..fff028d0e 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 74e0d11b8..e60bdd43e 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}; @@ -356,3 +357,23 @@ pub trait VirtioDevice: Send { false } } + +/// 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 76d044dc82b154ca9ebf7c92245f667cc9554ea6 Mon Sep 17 00:00:00 2001 From: louquan18 <2710830093@qq.com> Date: Sat, 17 Sep 2022 22:11:41 +0800 Subject: [PATCH 0172/1723] 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 226a4874b..702f0b1ae 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, @@ -1609,7 +1609,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 4b25e3c2b..45fb02e31 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 383f599a7db7adfa0ebea29408ff351db08da203 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 30 Sep 2022 14:21:58 +0800 Subject: [PATCH 0173/1723] 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 42e866522efdf16fc7ae0412f382c718eed09913 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 8 Oct 2022 16:10:34 +0800 Subject: [PATCH 0174/1723] 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 9072263312b5b65153f0ce17ddec00310d1db434 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 9 Oct 2022 19:28:11 +0800 Subject: [PATCH 0175/1723] 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 82566cd80b65fa8901bd9e20949aef36b2b0aab1 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:38:53 +0800 Subject: [PATCH 0176/1723] 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 d54caa91f16161415b41e1a429af1765b3c1ac2e Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:39:19 +0800 Subject: [PATCH 0177/1723] 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 141a9dedb3ebe1bbdc296d75922f19a4cdeab803 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:39:28 +0800 Subject: [PATCH 0178/1723] 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 1e1cfd8e6c443357cd14a59422ca27d68d0529c8 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:41:36 +0800 Subject: [PATCH 0179/1723] 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 f0403baad95ca7daa23ffa54adb88a835609b256 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:41:49 +0800 Subject: [PATCH 0180/1723] 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 6fb1794684a7b42f0ba07a39cd149c37ce248ed6 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:41:58 +0800 Subject: [PATCH 0181/1723] 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 2b3a0f6e368a0c8473fbe39754586bb5555b2f12 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:42:07 +0800 Subject: [PATCH 0182/1723] 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 83892d08280a7c84d50909b4347e35328b7139ec Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:42:14 +0800 Subject: [PATCH 0183/1723] 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 80272f37052fe7a1a0be7f8227e6ebe6a9f32c40 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:42:23 +0800 Subject: [PATCH 0184/1723] 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 b3ea000a5cba98a74e82f672ec753d5984b68625 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:42:30 +0800 Subject: [PATCH 0185/1723] 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 c99c876f2f55c896f779b9ddc92d80e9a001404f Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:42:36 +0800 Subject: [PATCH 0186/1723] 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 612f7c32cdc865c9e11d8815daf9f7c9a7b8882d Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:42:44 +0800 Subject: [PATCH 0187/1723] 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 3cd609b8df73d793181c6992bb83eb15ab7078a6 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 9 Oct 2022 05:17:12 -0400 Subject: [PATCH 0188/1723] 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 28d6d2df4e07d4a38f31358f74e16e7595d461b6 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 20 Sep 2022 20:43:11 +0800 Subject: [PATCH 0189/1723] 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 48c28feb0351452dbf0d3658ee8537e4aa8be964 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 8 Oct 2022 19:43:48 +0800 Subject: [PATCH 0190/1723] 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 412c9813c..a00668396 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 { @@ -881,39 +921,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 526dd64b5ef734f36066699d7b3a48852be9ee93 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 10 Oct 2022 10:03:32 +0800 Subject: [PATCH 0191/1723] 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 8d4bcbf3a1d487ef8cbcf4d2fcc68a31699ff348 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 12 Oct 2022 16:43:39 +0800 Subject: [PATCH 0192/1723] 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 6de6c74a29f1272b69040d4033d182549016fc13 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 12 Oct 2022 16:34:55 +0800 Subject: [PATCH 0193/1723] 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 bd97f386ca67c3e4a67fd7fe61c256a210a86c3a Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Tue, 11 Oct 2022 14:30:29 +0800 Subject: [PATCH 0194/1723] 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 7d328032f0b6198185c9ad97ecc3d928a12e8594 Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Tue, 11 Oct 2022 21:56:13 +0800 Subject: [PATCH 0195/1723] 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 4e12d9c34..aff178cd5 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 fcbfb0bf161c7901c720813f14de87efd92484da Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Tue, 11 Oct 2022 22:11:20 +0800 Subject: [PATCH 0196/1723] 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 213a869b53b4715c3c77f8fa044f7a761feca1eb Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 12 Oct 2022 16:21:28 +0800 Subject: [PATCH 0197/1723] 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 48a232b68..209a8205f 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 2e54252d9cd34561a6e71bc32961239d6f839a35 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 13 Oct 2022 15:06:13 +0800 Subject: [PATCH 0198/1723] 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 00495b310d57e907d55c8a264dd631b11dc573eb Mon Sep 17 00:00:00 2001 From: Zuo Xiaoxian Date: Thu, 13 Oct 2022 16:40:09 +0800 Subject: [PATCH 0199/1723] 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 920c9ca6fa71f258361beb1ee1846652acea7260 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 13 Oct 2022 14:44:37 +0800 Subject: [PATCH 0200/1723] 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 6d6ef48dfc1283aace4c6649d82467ab80c65e99 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 13 Oct 2022 15:37:46 +0800 Subject: [PATCH 0201/1723] 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 a209563859458358a658797ff918f4d392eee294 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 13 Oct 2022 15:55:31 +0800 Subject: [PATCH 0202/1723] 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 842585640f67401dca154d9153f9bb62c09a5d24 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 13 Oct 2022 11:42:51 +0800 Subject: [PATCH 0203/1723] 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 9496532949b017b476b727bbde00a7915c83d903 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 13 Oct 2022 14:14:35 +0800 Subject: [PATCH 0204/1723] 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 bba734a93aba534ea281567d94ab738a83472591 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 13 Oct 2022 20:49:05 +0800 Subject: [PATCH 0205/1723] 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 223bdef142d5fd1933c637ae9a9362a93d145663 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 13 Oct 2022 18:28:31 +0800 Subject: [PATCH 0206/1723] 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 db4d5839b3b0d14962b18e869e7c25df9863e109 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 17 Oct 2022 20:46:07 +0800 Subject: [PATCH 0207/1723] 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 56800c6c3778af6b20de8ecd00ae5b2e0873f3c1 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 19 Oct 2022 10:44:33 +0800 Subject: [PATCH 0208/1723] 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 221e6494b7b5464a8a974dc824dbbefdc7f5cf5c Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 17 Oct 2022 16:59:57 +0800 Subject: [PATCH 0209/1723] 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 aa5bb271d..64078cdf5 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 a869e4a702e50ea72d26b0a0e74c6463f0a13e2e Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 19 Oct 2022 17:51:38 +0800 Subject: [PATCH 0210/1723] 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 866827f5dcc13edfa7644b96f59c2f29a7137982 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 20 Oct 2022 15:37:58 +0800 Subject: [PATCH 0211/1723] 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 21ff896bebf44c287d89d5f5057d19e94d7224aa Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 20 Oct 2022 17:35:09 +0800 Subject: [PATCH 0212/1723] 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 0bcb0ceb961c72f05d5870767318dcf74f13c595 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 20 Oct 2022 18:54:42 +0800 Subject: [PATCH 0213/1723] 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 240fb1952c339dea6f57329cd09a1d87af47d707 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 20 Oct 2022 19:09:02 +0800 Subject: [PATCH 0214/1723] 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 947d2f9dc56af7a738e6ec7f2a09a22b8c30a634 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Fri, 21 Oct 2022 15:53:12 +0800 Subject: [PATCH 0215/1723] 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 e96e771a4ebefa783f0552461f63eb3c32080e21 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Fri, 21 Oct 2022 15:58:51 +0800 Subject: [PATCH 0216/1723] 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 a025019cfcb6e6b6e630f6d90e1212fb2ae47f82 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 20 Oct 2022 20:17:02 +0800 Subject: [PATCH 0217/1723] 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 bef8ef5397024ff67d1dd3cca3d118e635260bbf Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 20 Oct 2022 20:22:33 +0800 Subject: [PATCH 0218/1723] 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 d9ab9d907ac3982e2d5d75c6fc4823a77e4bcf0c Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 14 Oct 2022 11:03:45 +0800 Subject: [PATCH 0219/1723] 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 dc48984a114d37fe6bdf0d7e6746e9c0950b627f Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 22 Oct 2022 11:07:18 +0800 Subject: [PATCH 0220/1723] 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 82518d3a2..c4021f477 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 c59a5449a2003ef2aa39b5456d160a6d5f9587f8 Mon Sep 17 00:00:00 2001 From: lin Date: Sun, 9 Oct 2022 21:53:27 +0800 Subject: [PATCH 0221/1723] 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 6d0c87e8d2f2caa41892bb503e0ac41a7e898d7b Mon Sep 17 00:00:00 2001 From: lin Date: Thu, 29 Sep 2022 19:04:50 +0800 Subject: [PATCH 0222/1723] 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 b1ba1ce47fdc793cd158a05994308f6892eabb0c Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 25 Oct 2022 09:41:46 +0800 Subject: [PATCH 0223/1723] 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 111ea3d98945dd568e7e5d70a443986e0ef786fc Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 25 Oct 2022 09:55:15 +0800 Subject: [PATCH 0224/1723] 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 0b6eebf03535fbe1ea5ffb997224fd1a06887fdc Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 25 Oct 2022 10:04:07 +0800 Subject: [PATCH 0225/1723] 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 ff3e949fa5fe2168696d74b52afd8f677e1d0463 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 25 Oct 2022 11:04:58 +0800 Subject: [PATCH 0226/1723] 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 8a3011b2655834d346760de6fb38418615310e3a Mon Sep 17 00:00:00 2001 From: MJY-HUST Date: Wed, 26 Oct 2022 10:48:20 +0800 Subject: [PATCH 0227/1723] 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 60e945f0d..9f2bd4e41 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 c5d45d605..408708981 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, KVM_MEM_READONLY}; 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}; /// Request type of listener. #[derive(Debug, Copy, Clone)] @@ -131,9 +130,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(()) })?; @@ -148,7 +148,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. @@ -171,7 +171,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`, @@ -186,17 +186,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) )?; @@ -219,8 +221,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(()); @@ -236,7 +238,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 @@ -246,7 +248,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) { @@ -263,7 +265,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() @@ -273,8 +275,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(), @@ -302,7 +304,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, @@ -323,7 +325,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() @@ -331,7 +333,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(), @@ -367,7 +369,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(), @@ -404,7 +406,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(), @@ -460,20 +462,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))) } } @@ -511,7 +514,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(), @@ -548,7 +551,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(), @@ -603,14 +606,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 00c3aa406..b1bfa1db7 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, @@ -325,7 +330,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); @@ -418,7 +423,7 @@ impl Region { .filter(|end| *end <= self.size()) .is_none() { - return Err(ErrorKind::Overflow(addr).into()); + return Err(anyhow!(AddressSpaceError::Overflow(addr))); } Ok(()) } @@ -447,18 +452,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 { @@ -473,25 +480,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(()) @@ -519,7 +535,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, @@ -535,24 +551,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(()) @@ -592,10 +612,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, @@ -625,7 +645,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"); } @@ -647,7 +667,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(); @@ -663,14 +683,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"); } @@ -713,7 +735,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(), @@ -725,7 +747,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(), @@ -836,7 +858,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(), @@ -847,7 +869,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 aded05c7a..45d386741 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; @@ -459,7 +456,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(); @@ -531,26 +528,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(); @@ -559,17 +561,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(()) } @@ -656,7 +664,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 }); @@ -674,7 +682,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 }); @@ -689,7 +697,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 }); @@ -795,10 +803,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() + ))) } } @@ -818,7 +832,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 { @@ -845,7 +859,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(()) } @@ -906,7 +920,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 @@ -915,7 +929,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(()) } @@ -931,7 +945,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 { @@ -940,7 +954,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")?; } } } @@ -966,7 +980,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(); @@ -1017,7 +1034,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(()) } @@ -1025,7 +1042,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( @@ -1045,11 +1062,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; } } @@ -1238,7 +1251,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); @@ -1374,7 +1389,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 76434780a..12900424c 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. @@ -213,7 +213,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()) { @@ -250,7 +250,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())?; @@ -269,7 +269,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(()) } @@ -277,7 +279,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(()) } @@ -286,7 +290,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) } @@ -294,7 +300,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(()) } @@ -322,7 +330,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(()) } @@ -334,7 +344,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(()) } @@ -344,7 +356,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 )) @@ -352,7 +364,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 )) @@ -360,10 +372,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 { @@ -377,7 +389,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(()) } @@ -389,7 +403,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(()) } @@ -402,7 +418,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) } @@ -414,7 +432,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(()) } @@ -426,7 +446,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(()) } @@ -480,8 +502,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 b641cedec..658395950 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; @@ -216,7 +214,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(()); } @@ -247,7 +245,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(()); } @@ -304,7 +302,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())?; @@ -334,7 +332,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 @@ -344,7 +342,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(); @@ -384,13 +382,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) { @@ -401,12 +399,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) { @@ -414,27 +413,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, @@ -443,7 +442,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, @@ -458,7 +457,7 @@ impl VhostUserClient { pub fn delete_event(&self) -> Result<()> { self.delete_evt .write(1) - .chain_err(|| ErrorKind::EventFdWrite)?; + .with_context(|| anyhow!(VirtioError::EventFdWrite))?; Ok(()) } @@ -491,10 +490,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) } @@ -511,7 +510,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(()) } @@ -537,10 +536,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) } @@ -555,7 +554,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(()) } @@ -569,10 +568,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) } } @@ -586,7 +585,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(()) } @@ -600,10 +599,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) } @@ -619,7 +618,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(()) } @@ -650,7 +649,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(()) } @@ -675,7 +674,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(()) } @@ -692,13 +691,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 )) @@ -707,7 +706,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 )) @@ -723,7 +722,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(()) } @@ -748,7 +747,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(()) } @@ -772,7 +771,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(()) } @@ -796,7 +795,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(()) } @@ -821,7 +820,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 0cce0d0ab0c5b6bfc88bb74a9aec658e67463104 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 25 Oct 2022 14:17:27 +0800 Subject: [PATCH 0228/1723] 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 13311b6543b125f20bebfd09af281e75d75834c2 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 25 Oct 2022 14:34:30 +0800 Subject: [PATCH 0229/1723] 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 c2edb3b34261f388a07d22307e6a079e0e72e052 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 25 Oct 2022 14:40:35 +0800 Subject: [PATCH 0230/1723] 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 7d5a26fdbf0b567458c6754c72af6b77e5af2910 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 25 Oct 2022 14:46:26 +0800 Subject: [PATCH 0231/1723] 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 4ee1d41cc94d9cf6cd4f79e890e1860813e2a159 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 27 Oct 2022 16:00:44 +0800 Subject: [PATCH 0232/1723] 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 6d3767bd1cd750b21b836ba2cd9f141ef3dad747 Mon Sep 17 00:00:00 2001 From: Darui Zhuo Date: Sat, 6 Aug 2022 13:20:16 +0800 Subject: [PATCH 0233/1723] 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 581fc24d0..98e674a70 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. irq_chip: Option>, /// Memory address space. @@ -172,6 +176,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, @@ -499,6 +504,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() @@ -510,6 +520,7 @@ impl MachineOps for StdMachine { &CPUTopology::new(), &vcpu_fds, &boot_config, + &cpu_config, )?); if let Some(boot_cfg) = boot_config { @@ -809,6 +820,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()); } @@ -1189,6 +1201,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 { @@ -1265,6 +1295,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 c854ed40b6cbdae0f5aebf29fae148e6983a1635 Mon Sep 17 00:00:00 2001 From: Darui Zhuo Date: Sat, 17 Sep 2022 20:19:31 +0800 Subject: [PATCH 0234/1723] 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 7264c06f3..ba475050f 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 62a7f4658c8f15638aebb4c3857f37fd4412f0a7 Mon Sep 17 00:00:00 2001 From: Darui Zhuo Date: Wed, 21 Sep 2022 08:43:49 +0800 Subject: [PATCH 0235/1723] 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 fbe665d4c508d5fb5f3744c30f4cd0f19ee28d13 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 28 Oct 2022 11:49:09 +0800 Subject: [PATCH 0236/1723] 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 83629d2f093e97b25811c2f91849c1d64774c90e Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 15 Sep 2022 17:00:25 +0800 Subject: [PATCH 0237/1723] 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 c3a8e1528..0cd1f27c6 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -50,10 +50,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, @@ -77,9 +77,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 { @@ -614,6 +614,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)?; @@ -1041,6 +1052,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 1ec4f6e4d9603efa6427555407609c3a9d2dd7b5 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 17 Sep 2022 15:41:39 +0800 Subject: [PATCH 0238/1723] 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 0cd1f27c6..8ca58a429 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -77,10 +77,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. @@ -305,6 +306,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 @@ -619,6 +625,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 98e674a70..c64f97bdf 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -73,6 +73,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 { @@ -149,6 +150,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 { @@ -199,6 +202,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())), }) } @@ -639,6 +643,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 303f24753097c40b72b4e3ec52e3d1b0d9e0770b Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 17 Sep 2022 16:54:48 +0800 Subject: [PATCH 0239/1723] 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 8ca58a429..4de35e183 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -50,7 +50,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, @@ -78,10 +78,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. @@ -641,6 +642,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)?; @@ -1071,6 +1118,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 aea512885e685e0835f171fdeee7b2d95fdc1a0b Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 20 Sep 2022 15:31:22 +0800 Subject: [PATCH 0240/1723] 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 c188042bc07486026f37cf3444db0d137c39d4cd Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 20 Sep 2022 16:43:47 +0800 Subject: [PATCH 0241/1723] 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 e264c841bd5f2839be9ad77a606df1cb73606910 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 21 Sep 2022 15:01:04 +0800 Subject: [PATCH 0242/1723] 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 035cecab24df92a996483d1bf81fa72eba344802 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 23 Sep 2022 14:25:03 +0800 Subject: [PATCH 0243/1723] 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 625f1041a58babdf1092a80868d1099ca61abdc3 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 9 Oct 2022 09:30:43 +0800 Subject: [PATCH 0244/1723] 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 dd8a42532ebb7abfa0bb6be77b23f4e031a5ffd7 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 26 Sep 2022 18:13:31 +0800 Subject: [PATCH 0245/1723] 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 c33471202b6c6ff4fa69c2e282cacf1846395cec Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 9 Oct 2022 14:18:41 +0800 Subject: [PATCH 0246/1723] 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 ba475050f..4f012a641 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -753,6 +753,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 76572b6fd2a51b5ab4d4330446b5ede55e9e3355 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Fri, 28 Oct 2022 11:42:05 +0800 Subject: [PATCH 0247/1723] 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 704b4272ae05ceded28f300d249a3fbe82e7d316 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Sat, 29 Oct 2022 12:30:58 +0800 Subject: [PATCH 0248/1723] 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 310a1bf461de462f1870de30523dbeec5bc47e29 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Sat, 29 Oct 2022 12:35:48 +0800 Subject: [PATCH 0249/1723] 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 92bb7ce6b6bd0d5ee5740b05714ba3121e29b8cf Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 20 Oct 2022 11:16:10 +0800 Subject: [PATCH 0250/1723] 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 9fdbe9bd40f1ff1a1edb18d5255d993277612066 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 20 Oct 2022 11:43:09 +0800 Subject: [PATCH 0251/1723] 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 15050fd9dbc1663b8bc1d094ddece0040cf2078d Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 6 Sep 2022 20:56:59 +0800 Subject: [PATCH 0252/1723] 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 0cd4ee112a73ce836faa7fb9ae7eac8b5e0d6c60 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 20 Oct 2022 15:48:31 +0800 Subject: [PATCH 0253/1723] 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 7bc11096def6381b348d8b2e27cae93df4d6ecee Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 28 Oct 2022 10:16:39 +0800 Subject: [PATCH 0254/1723] 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 309945049a9bc34d83689e276ac62aa401442f62 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 31 Oct 2022 14:30:06 +0800 Subject: [PATCH 0255/1723] 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 16b35a2b0..6615ed38c 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -442,6 +442,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 41399565a6d891833be7aa85b561f0856ed67997 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 31 Oct 2022 14:22:55 +0800 Subject: [PATCH 0256/1723] 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 a465c1afc7556a2226883566c46b2b33e67a9478 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 31 Oct 2022 14:26:07 +0800 Subject: [PATCH 0257/1723] 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 7eee21a61d31b5ffbe131f6180153b2c7d8f3275 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 31 Oct 2022 14:32:46 +0800 Subject: [PATCH 0258/1723] 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 6a3e2606583328e6be6ecb99b75c85682c5e7253 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 26 Oct 2022 14:56:16 +0800 Subject: [PATCH 0259/1723] 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 c7b8e2d3958c9fa36cb5a966bcd0deaf0ed07bbd Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 31 Oct 2022 10:22:48 +0800 Subject: [PATCH 0260/1723] 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 634781ea2bd5e95bb1f61ac3377e843c7f75d9e8 Mon Sep 17 00:00:00 2001 From: Zuo Xiaoxian Date: Mon, 31 Oct 2022 20:43:45 +0800 Subject: [PATCH 0261/1723] 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 d0667b106e42d56dc721187e26e996bbe61e2155 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Fri, 28 Oct 2022 03:19:43 -0400 Subject: [PATCH 0262/1723] 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 6e36b010fbddb17f9798a4d3acb175e61206e622 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 31 Oct 2022 19:27:20 +0800 Subject: [PATCH 0263/1723] 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 784e662c2ba9e042db77ef6d474784cbe4b7f11d Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 26 Oct 2022 14:56:31 +0800 Subject: [PATCH 0264/1723] 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 b52c15e352b7f43705e246d90ac9389fec55e8b5 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 18 Oct 2022 13:52:55 -0400 Subject: [PATCH 0265/1723] 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 5ff24421ff7003679e157f7e77d87200ccaddc6d Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 18 Oct 2022 03:32:36 -0400 Subject: [PATCH 0266/1723] 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 9e8a67a6303b719aac4ab92643330666e27fd922 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 24 Oct 2022 15:42:05 -0400 Subject: [PATCH 0267/1723] 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 c17558209fcd5446ab6f2107175ef8b48f4e0368 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 24 Oct 2022 17:04:01 -0400 Subject: [PATCH 0268/1723] 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 42ffe231338a9ac339221522d474163f2a810436 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 31 Oct 2022 03:20:01 -0400 Subject: [PATCH 0269/1723] 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 14a6614ab5b1783a92edd5dd7981dba514d172e3 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 31 Oct 2022 03:46:51 -0400 Subject: [PATCH 0270/1723] 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 684ffe105a8f5d5d974cc4922e8fb8a1494bdc0f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 24 Oct 2022 11:32:25 -0400 Subject: [PATCH 0271/1723] 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 006702c65de60baba048814b99021cd9dd5f993b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 24 Oct 2022 20:40:29 -0400 Subject: [PATCH 0272/1723] 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 5dbca629b1b123096441a1d2b284adb6595d7239 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 24 Oct 2022 20:46:42 -0400 Subject: [PATCH 0273/1723] 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 644fa3793fdc580b0083e260a66acf98cec5d297 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 24 Oct 2022 20:50:29 -0400 Subject: [PATCH 0274/1723] 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 9f33927f34f4680dd90720621e1c33ccc128242f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 25 Oct 2022 01:19:02 -0400 Subject: [PATCH 0275/1723] 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 ddaba6e17ab67c931db1355fa3842bfd33580c47 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 25 Oct 2022 03:43:26 -0400 Subject: [PATCH 0276/1723] 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 6fcddc17af09587f937d44131a0a776a347df740 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 29 Oct 2022 22:22:44 +0800 Subject: [PATCH 0277/1723] 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 2a956a5385899af169d64ad8f64c0d922a302e75 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Tue, 1 Nov 2022 12:51:48 +0800 Subject: [PATCH 0278/1723] 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 09ca8e92d6a419a0f7f0dbc2238375243e63968b Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 29 Oct 2022 22:19:14 +0800 Subject: [PATCH 0279/1723] 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 4115cc5084f41ad3f178c6b60831b8606c57298e Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 1 Nov 2022 20:20:07 +0800 Subject: [PATCH 0280/1723] 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 473b6b314840c0b2c40b205a6f024e6c4f207e9b Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 2 Nov 2022 21:02:14 +0800 Subject: [PATCH 0281/1723] 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 bdc8b9888..5ef9d21f6 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 7ba21abbed26b4d3c2d9c97b43dbcc0b5c38f9cc Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Wed, 2 Nov 2022 11:31:47 +0800 Subject: [PATCH 0282/1723] 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 cfb7cd16a8aa02da4068bfa173c627ca07c033ed Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Wed, 2 Nov 2022 15:03:18 +0800 Subject: [PATCH 0283/1723] 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 b866a1d06d9a733242a23f423bf32bb78a6ba422 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Wed, 2 Nov 2022 16:01:15 +0800 Subject: [PATCH 0284/1723] 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 2db2956acd120eaf8ac699e46ff10bc1c00c8bb2 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 18 Aug 2022 20:42:34 +0800 Subject: [PATCH 0285/1723] 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 f212f1dec7d5a4771b69d34ae4374803c53de25f Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 26 Aug 2022 17:33:34 +0800 Subject: [PATCH 0286/1723] 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 1799606df57eec3d3009f726664543278e43f203 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 29 Oct 2022 10:16:07 +0800 Subject: [PATCH 0287/1723] 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 132792b0e03e185e95dda877845b1320889a4768 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 3 Nov 2022 15:26:58 +0800 Subject: [PATCH 0288/1723] 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 a30e98779f8d933839d19358e57625e3c12bad6c Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 3 Nov 2022 17:01:01 +0800 Subject: [PATCH 0289/1723] 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 e6b67284898d161c84133037992ba15f6301f42c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 24 Oct 2022 12:04:14 -0400 Subject: [PATCH 0290/1723] 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 6cde479ca2497be0f4e941d84a4ec17747b359e6 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 24 Oct 2022 13:46:32 -0400 Subject: [PATCH 0291/1723] 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 31007b29864e50f88912494be6bed8a70e6b9130 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 24 Oct 2022 18:40:38 -0400 Subject: [PATCH 0292/1723] 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 c7c2dd2eb273c2fd6e0ed846c3567eefd453c4cb Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 25 Oct 2022 05:53:40 -0400 Subject: [PATCH 0293/1723] 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 5eed2528c7e1b5085fc5df1d04488248c1d89f57 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 25 Oct 2022 06:21:13 -0400 Subject: [PATCH 0294/1723] 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 ee5db2f2847ab6a2ad10b508aefa66b5e0e52789 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 25 Oct 2022 19:37:23 -0400 Subject: [PATCH 0295/1723] 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 f44828c3fb96f3d42d67e9d85b2a05771ac28655 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 25 Oct 2022 20:29:19 -0400 Subject: [PATCH 0296/1723] 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 af7a465133425c8b3983b2d3309b9f32e4b77115 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 25 Oct 2022 06:21:13 -0400 Subject: [PATCH 0297/1723] 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 1504c3a60d856ce98a1ce9335438c8a77cec0798 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 25 Oct 2022 20:17:17 -0400 Subject: [PATCH 0298/1723] 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 2d2ce3631b84924e52d4f409c8376007f4bea784 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 29 Oct 2022 22:10:22 +0800 Subject: [PATCH 0299/1723] 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 fa6a394b2888b5c6bf24c1747288a7b0bf179301 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 30 Oct 2022 10:53:10 +0800 Subject: [PATCH 0300/1723] 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 9431f3976125907b51623cf654cb3cdb0004f5c3 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 30 Oct 2022 14:35:55 +0800 Subject: [PATCH 0301/1723] 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 96fe5ecb83738b05b1e561773fc0aff955f721c0 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 30 Oct 2022 15:43:38 +0800 Subject: [PATCH 0302/1723] 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 b64fa700a332586da469ee4fdd42e2c9ab8ae7b6 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 3 Nov 2022 17:40:30 +0800 Subject: [PATCH 0303/1723] 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 6862a82a80cf2a5ab470abe8b8fea86ac570bde5 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 3 Nov 2022 17:08:39 +0800 Subject: [PATCH 0304/1723] 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 4a6d15e14b0b388d03a9db3064868fd99c7118bd Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 4 Nov 2022 15:18:46 +0800 Subject: [PATCH 0305/1723] 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 2759a975128403ac5ec533a5536dc1079d00aa60 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 31 Oct 2022 01:06:03 -0400 Subject: [PATCH 0306/1723] 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 e919a5d6a61e9f49148180572195d5c9aa39286a Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 7 Nov 2022 19:39:18 +0800 Subject: [PATCH 0307/1723] 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 6d06e8dac61b3b9df1302769607173b3e2a4db92 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sun, 30 Oct 2022 23:55:45 -0400 Subject: [PATCH 0308/1723] 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 7cf99e49c..6363c3a38 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 5aa5762d911603b9d24360b413b18eb81639e78b Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 31 Oct 2022 02:18:14 -0400 Subject: [PATCH 0309/1723] 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 159af5c3bb86a6f4067b14271144c1c8f74ad9be Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 7 Nov 2022 14:43:48 +0800 Subject: [PATCH 0310/1723] 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 ee803c6524343e019bdd0e17faa3c53f867083b7 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 7 Nov 2022 15:20:46 +0800 Subject: [PATCH 0311/1723] 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 ee497fea8..1b505cd4d 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -1053,9 +1053,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; @@ -1063,7 +1071,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; @@ -1086,6 +1094,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 ce6356fbb99b2718bf60e8ec83379610ba7c2ea6 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 7 Nov 2022 12:25:18 +0800 Subject: [PATCH 0312/1723] 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 0f37fcacaffd7a614df4fbd47dc17d73187cacab Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 7 Nov 2022 12:53:29 +0800 Subject: [PATCH 0313/1723] 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 1b505cd4d..ea8472556 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -1314,6 +1314,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 eceee7002..48f03c55d 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 edd5bca8d0a50aaed94e413e631ce15bc0f268d1 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 31 Oct 2022 01:47:59 -0400 Subject: [PATCH 0314/1723] 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 22ecfb6650cda96da0ceec3470d6bdf54477646a Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sun, 30 Oct 2022 21:49:29 -0400 Subject: [PATCH 0315/1723] 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 6e6b97f7b..73c8e1bd0 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -513,7 +513,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 66467547f..8c8172843 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -1075,7 +1075,7 @@ impl PciDevOps for VirtioPciDevice { RegionType::Mem32Bit, false, mem_region_size, - ); + )?; self.device .lock() -- Gitee From 0ab1d3cb69cfe0f5613aff1a2b5827502b4809a2 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sun, 30 Oct 2022 21:57:10 -0400 Subject: [PATCH 0316/1723] 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 0fc3efdf3f652cf84cee27535bbccaf42ac59d1f Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sun, 30 Oct 2022 23:12:37 -0400 Subject: [PATCH 0317/1723] 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 73c8e1bd0..d62c673bb 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, @@ -504,7 +505,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 8c8172843..00891e72d 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::{ @@ -1065,7 +1066,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 3506b4677d13e9b7ebd99e796f6c54d68fa9a5a7 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 7 Nov 2022 10:24:34 -0500 Subject: [PATCH 0318/1723] 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 fd65ad87b121a7d9f7bdd324f969a29fb6afb741 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 3 Nov 2022 17:25:57 +0800 Subject: [PATCH 0319/1723] 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 9ea1c31bcf09a7a7428c6f70a5c4ae69a981a139 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 7 Nov 2022 20:08:45 +0800 Subject: [PATCH 0320/1723] 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 2b4fcea31a6e4794bc84f5dfe9d30db327341620 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 3 Nov 2022 15:38:20 +0800 Subject: [PATCH 0321/1723] 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 3207da5e35e3690ff6dfb96b730cf3b0c79c7e2b Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 3 Nov 2022 17:39:37 +0800 Subject: [PATCH 0322/1723] 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 075a2b81238f55cf9dcc138fac104331d855dc4a Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 9 Nov 2022 09:13:21 +0800 Subject: [PATCH 0323/1723] 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 c65397ff46734822c779e0f2da16e5ace2c04c33 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 8 Nov 2022 21:04:05 +0800 Subject: [PATCH 0324/1723] 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 3048f38470c6c187cc4ee4bd0bbf1f5c49d98785 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 3 Nov 2022 21:52:18 +0800 Subject: [PATCH 0325/1723] 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 1ba6d87041e62b106a03dd5621e1fac2007e370e Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 3 Nov 2022 21:58:49 +0800 Subject: [PATCH 0326/1723] 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 0aa4ed1d737377e6c40a03edbbad494f07c33ed0 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Fri, 4 Nov 2022 10:02:41 +0800 Subject: [PATCH 0327/1723] 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 9aafb22822cc90a7ebdcc2753a9b5c8e9a22b1a7 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 8 Nov 2022 19:59:42 +0800 Subject: [PATCH 0328/1723] 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 5b545eec16ce03bbfd3f5f7506eb82e5cffb9384 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 9 Nov 2022 17:26:34 +0800 Subject: [PATCH 0329/1723] 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 f06f5b6bba007da600f7e13bdd8dc7081186a70d Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 9 Nov 2022 19:15:12 +0800 Subject: [PATCH 0330/1723] 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 282e963bab6458d314ad9e36eb7c90c5f0d29170 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 2 Nov 2022 09:57:04 +0800 Subject: [PATCH 0331/1723] 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 25e62632d9cb29568f600e2993851562df16fc78 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 2 Nov 2022 10:26:17 +0800 Subject: [PATCH 0332/1723] 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 7b8b179137bc9ab623ed1744983877d5880f4a92 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 2 Nov 2022 15:57:41 +0800 Subject: [PATCH 0333/1723] 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 e152cbd97c8707de3e405b95a2ff8d97a5ee01e0 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 3 Nov 2022 10:42:34 +0800 Subject: [PATCH 0334/1723] 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 e76637a2a..630bf0dee 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 91a8b406b33959846e7ab08460e1b5fdbc3103f8 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 3 Nov 2022 10:52:29 +0800 Subject: [PATCH 0335/1723] 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 92e0dc547627eadce31a120e647b6d709062d5dc Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 3 Nov 2022 11:43:56 +0800 Subject: [PATCH 0336/1723] 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 6dac78db6f975fd635ed7ab699fba68afcb290d2 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 5 Nov 2022 10:23:27 +0800 Subject: [PATCH 0337/1723] 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 b6498eba859c4411bfdc562ae2360fb5a09f07d4 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 5 Nov 2022 11:43:43 +0800 Subject: [PATCH 0338/1723] 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 2af59ec00a6ca7faf1c2a36585174d523dbb17fe Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 7 Nov 2022 20:25:25 +0800 Subject: [PATCH 0339/1723] 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 7a30c14cc3a5fb5e0f87c48e2a09aaafd506c015 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 8 Nov 2022 02:00:23 -0500 Subject: [PATCH 0340/1723] 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 c5d15d5e2f02d9378c499c985f6f12b8248a13df Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 8 Nov 2022 02:26:01 -0500 Subject: [PATCH 0341/1723] 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 1732449c00de9b088acdb8f1eafe50132625288a Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 8 Nov 2022 02:45:57 -0500 Subject: [PATCH 0342/1723] 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 b5bb2d89d3cd009385aa0709b721fd84415c6958 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 8 Nov 2022 02:58:38 -0500 Subject: [PATCH 0343/1723] 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 c23b21fa111d4d2e5fdeac18a6f8a0fb67446dc7 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 8 Nov 2022 03:04:22 -0500 Subject: [PATCH 0344/1723] 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 359ac12a6361e7a5a8c5edf593dcd93caca4dbf2 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 11 Nov 2022 15:55:10 +0800 Subject: [PATCH 0345/1723] 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 71d8f1e8cef76c955567b1967a6aae28d32bb8ba Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 14 Nov 2022 12:42:33 +0800 Subject: [PATCH 0346/1723] 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 f7f53aae0389669ab891849586dd743cf28dd72e Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 14 Nov 2022 13:06:49 +0800 Subject: [PATCH 0347/1723] 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 453205db0394b592fbb3276f793d53f6474c3e56 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 14 Nov 2022 14:15:28 +0800 Subject: [PATCH 0348/1723] 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 9021b2409524cdbe835effa9013d9014cdf2907b Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 14 Nov 2022 14:25:11 +0800 Subject: [PATCH 0349/1723] 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 06be8c9e5918411793fc8a12349dc845d8efd4f2 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 14 Nov 2022 14:30:21 +0800 Subject: [PATCH 0350/1723] 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 1347a9b2a093af2054ae081b9458453a41885d6a Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 14 Nov 2022 14:40:36 +0800 Subject: [PATCH 0351/1723] 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 fbc3d255c1c6a8eb0aa4c549adf73aaa73613d42 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 14 Nov 2022 14:45:08 +0800 Subject: [PATCH 0352/1723] 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 0469d5e79a3de652577ab526a621ac8f08f6d8df Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 14 Nov 2022 14:46:34 +0800 Subject: [PATCH 0353/1723] 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 ff4c5bc4778f8bece217691fa396507bda864bab Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 14 Nov 2022 14:47:59 +0800 Subject: [PATCH 0354/1723] 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 122fb2efb83d939c2684b75c582c2e25e00d267a Mon Sep 17 00:00:00 2001 From: Zuo Xiaoxian Date: Tue, 15 Nov 2022 16:37:23 +0800 Subject: [PATCH 0355/1723] 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 ed758130f68d9615aa20b5ac20b45fee1cf0f15c Mon Sep 17 00:00:00 2001 From: Zuo Xiaoxian Date: Tue, 15 Nov 2022 17:08:46 +0800 Subject: [PATCH 0356/1723] 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 02f665bb9504cee7d8f9c6a934e182cb6226c9c6 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 15 Nov 2022 18:49:41 +0800 Subject: [PATCH 0357/1723] 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 21e15e961e1bf5a0fc07e4b1ef470a3a56b0612e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 5 Nov 2022 16:56:19 +0800 Subject: [PATCH 0358/1723] 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 086a4a2e86838738314b0a6dc35831f1657996ef Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 6 Nov 2022 05:39:08 +0800 Subject: [PATCH 0359/1723] 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 4de3e8ecd17871c87cd552e30a26e8bcbf4a918b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 5 Nov 2022 18:26:50 +0800 Subject: [PATCH 0360/1723] 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 2f7239e0b0866cf59119c1ea46bfe26c1ff98756 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 12 Nov 2022 07:37:46 +0800 Subject: [PATCH 0361/1723] 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 c48911cebeb0642c56be0446b1cd6436ae89aa00 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 9 Nov 2022 23:28:29 +0800 Subject: [PATCH 0362/1723] 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 1b591d73ec7f65851aef98de3580642fda2dbb01 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 6 Nov 2022 15:23:41 +0800 Subject: [PATCH 0363/1723] 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 55f014cf2af7e880a4470371979dde2734478f19 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 9 Nov 2022 16:21:18 +0800 Subject: [PATCH 0364/1723] 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 630bf0dee..584edae62 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -842,7 +842,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 @@ -850,7 +850,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 58b26eaa6affb00c99ae9ddc733e4aade6a2465e Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sat, 12 Nov 2022 07:07:38 -0500 Subject: [PATCH 0365/1723] 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 f146f8734..712e5719a 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 c660b3736f96d09f95be22ae70e3e15894bbdd25 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 15 Nov 2022 15:32:33 +0800 Subject: [PATCH 0366/1723] 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 c8dd2ec7800b567886f98d51e8e61ae63f587636 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 15 Nov 2022 15:40:59 +0800 Subject: [PATCH 0367/1723] 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 51a17c21b33dd7aa4797cd44d026a7d5742517af Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 7 Nov 2022 20:01:06 +0800 Subject: [PATCH 0368/1723] 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 d62c673bb..288c4147b 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -457,8 +457,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)?; @@ -557,6 +560,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 7724fe7e67f8e47a476501e22f4098c896d2b69c Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 9 Nov 2022 14:10:43 +0800 Subject: [PATCH 0369/1723] 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 584edae62..cb9acb98e 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -271,6 +271,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 { @@ -894,9 +908,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 4e8378b6ee7b686c5fbf84330473326ef45fc7df Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 9 Nov 2022 14:16:09 +0800 Subject: [PATCH 0370/1723] 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 cb9acb98e..dbc0501e0 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -350,8 +350,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 { @@ -381,7 +381,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, } } @@ -761,7 +761,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 6948e3749..e9fc2927b 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 1368313dbae8f13b2625d809939ab5222d7f0856 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 9 Nov 2022 14:56:52 +0800 Subject: [PATCH 0371/1723] 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 dbc0501e0..9519cc180 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -480,6 +480,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]); @@ -487,7 +488,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 288c4147b..fa686e2b7 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); @@ -663,7 +675,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 fc91297bf4b1e1f9c98ae495f928fe8defdc54ad Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sat, 12 Nov 2022 07:23:15 -0500 Subject: [PATCH 0372/1723] 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 712e5719a..96cc278f6 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -283,7 +283,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 7ea9a44d7b0dc41521c2c9a93e1586fe1a37eb3d Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sun, 13 Nov 2022 03:08:59 -0500 Subject: [PATCH 0373/1723] 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 9519cc180..709f20ae6 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 e9fc2927b..fe4a2888e 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.pci_express_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 8e6498bc9745e8ff05f2308c72039c687010dc1f Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sun, 13 Nov 2022 08:36:36 -0500 Subject: [PATCH 0374/1723] 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 2e7872fb0..c72d0b4e9 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 709f20ae6..10d12214c 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 d574039c83a7c616292c02740870faa1e09808e9 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 10 Nov 2022 11:28:18 +0800 Subject: [PATCH 0375/1723] 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 76c47435683645e0c9b3e604e31427d41a50915f Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 14 Nov 2022 20:23:26 +0800 Subject: [PATCH 0376/1723] 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 94f796f13..5baff5336 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; @@ -484,8 +485,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>>> { @@ -541,8 +542,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 6d0c122f8..b7cd867dc 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 7ee7a7882e03ec8eb53a6f39f8334d5a396be359 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 14 Nov 2022 21:00:56 +0800 Subject: [PATCH 0377/1723] 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 7b2b986ca80ad29087f486e1ee652e6261fa2103 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 15 Nov 2022 19:16:56 +0800 Subject: [PATCH 0378/1723] 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 d2a7f2dba90ae25b6b278bc1cfef8bb8604aa21e Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 15 Nov 2022 21:21:41 +0800 Subject: [PATCH 0379/1723] 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 767d58cdc5c82734397f898830ebfa02be80bc24 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 15 Nov 2022 21:35:47 +0800 Subject: [PATCH 0380/1723] 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 bb6ef9ccb8c41bc08fc7ee872a69e89b2141856d Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 16 Nov 2022 16:02:50 +0800 Subject: [PATCH 0381/1723] 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 5930215b38bfe606f4c911cb6fc2fcd18c360b89 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 16 Nov 2022 17:05:15 +0800 Subject: [PATCH 0382/1723] 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 d2cad0af7..1a1f70d7e 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 1c370ea14..a449bf7f8 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 9cf6336525acdc8bfa0ce22fdcbdbfa7b1eb9e4a Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 16 Nov 2022 17:20:29 +0800 Subject: [PATCH 0383/1723] 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 1a1f70d7e..88a6aa538 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, @@ -391,7 +391,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 6fcad846a909532e9951e6aa2041693fcc06f55d Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 16 Nov 2022 17:44:05 +0800 Subject: [PATCH 0384/1723] 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 3c0047c1971aec4d6fc82a978a8841205ea91539 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 16 Nov 2022 21:03:59 +0800 Subject: [PATCH 0385/1723] 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 ffa0de724320febb0a87b61fdae4e6832575fde8 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 17 Nov 2022 14:43:25 +0800 Subject: [PATCH 0386/1723] 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 72d5a6c78..af358197e 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -635,9 +635,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 37dd903947f0fd14bf17b95c74f916cd2398115c Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 12 Nov 2022 09:37:57 +0800 Subject: [PATCH 0387/1723] 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 3efcb49909aa3d7b9939e6ed437f842dfe9bc9f3 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 12 Nov 2022 09:55:04 +0800 Subject: [PATCH 0388/1723] 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 56303d2dd4580174ddb1da4c4140f1e9c9fa693d Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 12 Nov 2022 22:05:31 +0800 Subject: [PATCH 0389/1723] 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 e48dab14c6cc21146f05b279de8342d3e87bc931 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 14 Nov 2022 21:41:01 +0800 Subject: [PATCH 0390/1723] 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 8ae80c04e4da52d382292927287599c0e7c82913 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 14 Nov 2022 21:58:00 +0800 Subject: [PATCH 0391/1723] 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 56e50ce286be733d817ea70c0ca95c130be76b5c Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 15 Nov 2022 09:15:25 +0800 Subject: [PATCH 0392/1723] 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 957e0f85be51cacef1c6892fc8adfa6992470084 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 15 Nov 2022 09:58:43 +0800 Subject: [PATCH 0393/1723] 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 8e38946a5b577e306c2b59d538bed0ee69d053af Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 15 Nov 2022 10:09:12 +0800 Subject: [PATCH 0394/1723] 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 d65f94c9d8c71b55fb4b4dcb16541b06d651720d Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 15 Nov 2022 10:33:10 +0800 Subject: [PATCH 0395/1723] 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 d2ce31062f12cf017c2da0e6c531cce0dfc2b2e0 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 15 Nov 2022 14:44:33 +0800 Subject: [PATCH 0396/1723] 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 2d4bf0c43d38905c53129534cfbbc9d7d116812e Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 15 Nov 2022 14:39:01 +0800 Subject: [PATCH 0397/1723] 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 6deda588978e43fc4cac333b6be7b35d2cd8793a Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 17 Nov 2022 04:55:28 -0500 Subject: [PATCH 0398/1723] 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 af358197e..dcc481b8e 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 432399f71f87dced04bd45c3e30783115cc6d104 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 14 Nov 2022 21:57:27 -0500 Subject: [PATCH 0399/1723] 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 0fedbe30a6e97935c8a0d060484ea89c2b42fe15 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 14 Nov 2022 22:21:15 -0500 Subject: [PATCH 0400/1723] 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 6cbb14daf2d0bea5fa274e6812481a5dc3ff06a7 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 14 Nov 2022 22:23:16 -0500 Subject: [PATCH 0401/1723] 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 52ade64305631658eebe6516551e6d4f19de1cbb Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Thu, 10 Nov 2022 20:32:28 -0500 Subject: [PATCH 0402/1723] 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 8ab76f1ab2a12b5c2cb6676a768679cb2e3e7867 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Thu, 10 Nov 2022 21:24:43 -0500 Subject: [PATCH 0403/1723] 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 88d17fc3ca52056c7a27f6f84b20672d65e25f4e Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Wed, 16 Nov 2022 23:30:52 -0500 Subject: [PATCH 0404/1723] 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 73bd20a947e5f65546bb175d68d530c2d3f62b11 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Fri, 18 Nov 2022 02:03:17 -0500 Subject: [PATCH 0405/1723] 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 96d75791268e99e17a9a6dda01d26b6b105d2e67 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 6 Nov 2022 13:19:12 +0800 Subject: [PATCH 0406/1723] 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 3d5ddbdedc5135568c5dbe93dfb1624a068ed03a Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 15 Nov 2022 23:48:26 +0800 Subject: [PATCH 0407/1723] 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 67cc224f2b109f021ace5989bf7670b3e9ba4bd3 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 19 Nov 2022 15:02:56 +0800 Subject: [PATCH 0408/1723] 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 73387b73c061b83a4c5432a2dae31effbb9a23df Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 14 Nov 2022 22:33:55 -0500 Subject: [PATCH 0409/1723] 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 a86d9a7c4..ad2a05431 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -950,17 +950,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 9ef5a55bea359c77f1aa77c306064b7124998b07 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 21 Nov 2022 17:28:55 +0800 Subject: [PATCH 0410/1723] 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 af63cab0858a68a14dc80c29d5c27eb4e96d79a5 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 18 Nov 2022 09:51:06 +0800 Subject: [PATCH 0411/1723] 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 e1ee3f890d390b712588f22202936f219c613fa1 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 18 Nov 2022 10:10:52 +0800 Subject: [PATCH 0412/1723] 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 6ba1ff222cbd062b4f6710dd086a60a9cbfbb7a0 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 18 Nov 2022 15:50:24 +0800 Subject: [PATCH 0413/1723] 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 a4dca523761b7a301fabfde61252cfba73f07efd Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 16 Nov 2022 19:19:34 +0800 Subject: [PATCH 0414/1723] 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 a053a7be6edb24253c3962699af456e607d648a3 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 6 Nov 2022 14:27:23 +0800 Subject: [PATCH 0415/1723] 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 e37591e96653596d7140d276692f1aca39bef0b6 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 19 Nov 2022 18:57:41 +0800 Subject: [PATCH 0416/1723] 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 927211aa9af44e7cce195b379d48062106d1edba Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 16 Nov 2022 18:27:47 +0800 Subject: [PATCH 0417/1723] 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 38665d63b96eda22c2e81aa10a1282decfacaea3 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 16 Nov 2022 22:09:40 +0800 Subject: [PATCH 0418/1723] 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 493bb3c292840e1561e2b753274088d489fec24e Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 17 Nov 2022 11:18:21 +0800 Subject: [PATCH 0419/1723] 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 0b165d57bee304f73d7a666fac48c34cd4bcc0aa Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 17 Nov 2022 11:35:17 +0800 Subject: [PATCH 0420/1723] 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 ce54d8fab7498a8c247677154852b677240d336c Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 17 Nov 2022 17:16:43 +0800 Subject: [PATCH 0421/1723] 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 6fb5f524510b32bdbeb7cf4cedcb31c6584afe40 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 17 Nov 2022 20:33:35 +0800 Subject: [PATCH 0422/1723] 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 2fe58f16a861f4db9b1fb15c79222a56bc09db7f Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 18 Nov 2022 09:40:01 +0800 Subject: [PATCH 0423/1723] 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 f0cddae95cdf66fdc4673ec2e96fa6ae05a7d434 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 19 Nov 2022 18:16:14 +0800 Subject: [PATCH 0424/1723] 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 937357076cd05bab83bba7aa1ff6607ab3d5ddfb Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 22 Nov 2022 09:27:45 +0800 Subject: [PATCH 0425/1723] 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 8b7abd046..393104647 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -396,10 +396,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 b4b3c02c3ec4607d5775e1cb446e932c83d965ce Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 22 Nov 2022 10:21:57 +0800 Subject: [PATCH 0426/1723] 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 393104647..2bb79b97b 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; @@ -490,7 +491,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); @@ -536,7 +537,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 8b4d5247a9c95f5d9e8ed9ff0775dffd997e09b1 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 22 Nov 2022 11:29:49 +0800 Subject: [PATCH 0427/1723] 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 b64e0d247..1b3a6e77e 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -623,14 +623,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 => { @@ -649,44 +648,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 acb4cb1568518679e05b8a33d6cda84d89f9eea5 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 22 Nov 2022 14:25:49 +0800 Subject: [PATCH 0428/1723] 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 1b3a6e77e..39ff73468 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 @@ -1296,8 +1299,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; @@ -1352,7 +1358,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 { @@ -1442,7 +1448,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 { @@ -1500,7 +1506,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 973bb590c4198ed624796f63611042cc62ce29d4 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 22 Nov 2022 14:56:45 +0800 Subject: [PATCH 0429/1723] 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 39ff73468..286a55446 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -1372,9 +1372,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 2bc6599e9378a98084f8c1f3579c4ffb24243f27 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 22 Nov 2022 02:07:36 -0500 Subject: [PATCH 0430/1723] 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 7e0c7d11c4e02223e45188df32249ff978ea372a Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 22 Nov 2022 02:30:33 -0500 Subject: [PATCH 0431/1723] 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 0dcf06080f81baeb6f3dac51176a02ae59704595 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Wed, 23 Nov 2022 10:51:39 +0800 Subject: [PATCH 0432/1723] 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 fe3fd05a5edb11e5978a3bf452e73634b5dcd50f Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Wed, 12 Oct 2022 15:34:09 +0800 Subject: [PATCH 0433/1723] 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 10d0bbe068d4210e9f2f6340cdb4b7e79eb54494 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Wed, 12 Oct 2022 15:41:54 +0800 Subject: [PATCH 0434/1723] 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 73b5079df8ddddbd2177670e0153ed3dcafb1248 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Mon, 24 Oct 2022 20:02:55 +0800 Subject: [PATCH 0435/1723] 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 46e4d61e4b56cb6801b7f8bf0df61b613a334528 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 19 Nov 2022 21:31:51 +0800 Subject: [PATCH 0436/1723] 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 3ed316e42..1c7f5ad12 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 4d4568fb4c9305c40e635595e971d06666dd5ce4 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 19 Nov 2022 22:37:06 +0800 Subject: [PATCH 0437/1723] 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 1c7f5ad12..b91695ae6 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 f42436a4eed24e5c0f7b237bffb083fd6d782004 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 13 Nov 2022 01:31:35 +0800 Subject: [PATCH 0438/1723] 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 0d12b77ed131f91b1e3b08d29f91bfce83f7729d Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 12 Nov 2022 13:38:01 +0800 Subject: [PATCH 0439/1723] 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 d13f0060c..9d25dc803 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 b91695ae6..b80b21c33 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 ace8409e5..377696f96 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 6ab4301c4..c5fe06c12 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -903,7 +903,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 cd430aebadbc6215a08d67e2cbbde238f5970e0c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 12 Nov 2022 13:52:38 +0800 Subject: [PATCH 0440/1723] 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 9d25dc803..33bc0deea 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 9148c45c2f963c5507f63347628f1387eb3b0d5b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 13 Nov 2022 08:03:11 +0800 Subject: [PATCH 0441/1723] 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 5da7d21f4bb4c848560b390db52c3124eadcf7be Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 19 Nov 2022 15:31:07 +0800 Subject: [PATCH 0442/1723] 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 c90ce7f0e..ccc9f91c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -858,6 +858,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 82c21c1885d7b151e20946cd29818041a11d003f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 20 Nov 2022 16:41:02 +0800 Subject: [PATCH 0443/1723] 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 925ad50db68c5bf2cbd0f57fa56bf639e2878a28 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 20 Nov 2022 22:54:21 +0800 Subject: [PATCH 0444/1723] 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 967f202470fbcc6ae57ea8356f35adcd0c093c40 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 20 Nov 2022 21:05:37 +0800 Subject: [PATCH 0445/1723] 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 d1f675802fedb59483f3ecb4e3c64511a77394cc Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 20 Nov 2022 13:50:48 +0800 Subject: [PATCH 0446/1723] 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 a70c789a96600862ad6319edf0fed2ff99804b6a Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 19 Nov 2022 15:05:35 +0800 Subject: [PATCH 0447/1723] 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 7d1b7c580d8318f30812c4a1b5bf02206f56267e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 12 Nov 2022 18:25:05 +0800 Subject: [PATCH 0448/1723] 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 56bcb3c7f986bc23fc5e9d82f96f3136fb5f4df2 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 12 Nov 2022 23:57:31 +0800 Subject: [PATCH 0449/1723] 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 10be5a2cb06821fe6d54edf66df29ec4e1cf624c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 19 Nov 2022 14:34:24 +0800 Subject: [PATCH 0450/1723] 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 e908324f4424e3c50b5e95b99217c64f3fab59a3 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 12 Nov 2022 18:36:44 +0800 Subject: [PATCH 0451/1723] 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 62aa1b90d11ca7b797e09fb12002753530a069e2 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 19 Nov 2022 23:32:52 +0800 Subject: [PATCH 0452/1723] 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 5eca9c6163375121c385ffa3bd6ecdfa34f50f77 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 23 Nov 2022 10:04:11 +0800 Subject: [PATCH 0453/1723] 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 8f88c87f01e9198c3fdd1d661c07c38454f180b9 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 23 Nov 2022 11:29:41 +0800 Subject: [PATCH 0454/1723] 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 e838b145d4b62db8427f467d3836bce63b3acf9e Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Wed, 23 Nov 2022 01:34:30 -0500 Subject: [PATCH 0455/1723] 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 d259777f7..4a27ac57c 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 crate::cmdline::FsConfig; use std::fs::File; @@ -368,24 +374,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 75a07393abcbf419d265fff0cbdd4aac727ea0de Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 22 Nov 2022 21:51:50 -0500 Subject: [PATCH 0456/1723] 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 04d48121b4e35dc1e41bce3e455c7170d334668a Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 22 Nov 2022 23:26:10 -0500 Subject: [PATCH 0457/1723] 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 4a27ac57c..fbf5ff842 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -344,28 +344,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 98c7e7e1081473cee751c0d62c89fa1e0b6ad588 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 14 Nov 2022 20:04:03 +0800 Subject: [PATCH 0458/1723] 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 a363c3cadb346ee7f8d8a44f66c3d92a5bd7d47c Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 22 Nov 2022 20:10:26 +0800 Subject: [PATCH 0459/1723] 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 8b571681b99c8b897fbb625c126e8afbd1acb21a Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 24 Nov 2022 15:35:49 +0800 Subject: [PATCH 0460/1723] 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 ca159b2460f823469b6f2e8054d321d52acaf5c4 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Tue, 22 Nov 2022 07:58:07 +0800 Subject: [PATCH 0461/1723] 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 d8b718b6e..e064fd7c1 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -492,6 +492,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 @@ -500,6 +519,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 { @@ -508,6 +531,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 217c166cc..5b3359da3 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -253,16 +253,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 fc159d93b72a96076aed9a7d014f7728339bc6e8 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Tue, 22 Nov 2022 12:54:29 +0800 Subject: [PATCH 0462/1723] 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 e064fd7c1..c273c5635 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; @@ -488,11 +489,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(), @@ -519,7 +524,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 75608081b..b855dd89a 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -349,15 +349,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 5b3359da3..36d22a470 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -18,8 +18,7 @@ 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, - 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}; @@ -241,14 +240,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 6b78a2e26..c0596e71d 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -1250,17 +1250,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 0ee6a284f121ed1620472679dac90c6c6679bbdc Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 15 Nov 2022 01:11:18 +0800 Subject: [PATCH 0463/1723] 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 84317fedc4e3a9426d98520851281a2d916265d1 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 5 Sep 2022 21:38:57 +0800 Subject: [PATCH 0464/1723] 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 d81ad79e5..b91c551ac 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 0465/1723] 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 b5325c7afde03018dd51b7ef63789e7fb3a34c99 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 24 Nov 2022 20:39:37 +0800 Subject: [PATCH 0466/1723] 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 c9be6c86d5d5ac6ffc6820ecc3e3ecb81eb795e4 Mon Sep 17 00:00:00 2001 From: xuexiaowei Date: Thu, 24 Nov 2022 16:24:37 +0800 Subject: [PATCH 0467/1723] 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 3f194226f50ac013bafa30f7f5a9b2b91133f846 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Wed, 23 Nov 2022 03:46:04 -0500 Subject: [PATCH 0468/1723] 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 41f680b7b2521683ce285b42d7723b7cf3136648 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Wed, 23 Nov 2022 07:54:50 -0500 Subject: [PATCH 0469/1723] 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 f39d56629ae71e36d54f6be0298576d0987de614 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Wed, 23 Nov 2022 20:58:27 -0500 Subject: [PATCH 0470/1723] 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 a639c3526c58207d5c51cbc7a2b397898c4848a1 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Fri, 25 Nov 2022 10:13:58 +0800 Subject: [PATCH 0471/1723] 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 ed124dab2..d73b78b07 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 8802d19de..315e44485 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![ @@ -149,6 +149,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 2a76519a6..630173448 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 cc0df84e1848228f9d96a8cd50d869d5ec00096e Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Fri, 25 Nov 2022 10:43:50 +0800 Subject: [PATCH 0472/1723] 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 51b655b1d..2b1112856 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -117,3 +117,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 18c30483687d535fad44a9107a24763ea68774ed Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Fri, 11 Nov 2022 11:01:26 +0800 Subject: [PATCH 0473/1723] 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 bc6bfb13f5570d82a8b5e6d54b8dccec9530d723 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Fri, 25 Nov 2022 19:03:41 +0800 Subject: [PATCH 0474/1723] 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 096fbc528b99dd30e2b24d57ab3076fee90d593d Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 6 Nov 2022 11:26:19 +0800 Subject: [PATCH 0475/1723] 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 f6be1a83f73fd118dc07f038dfe57ddcb47d7094 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 24 Nov 2022 17:38:03 +0800 Subject: [PATCH 0476/1723] 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 31c7cca4f084c0c434b9fb3c15bb0c2eb49ed3a3 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 22 Nov 2022 11:46:15 +0800 Subject: [PATCH 0477/1723] 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 f2e0591116800ea430c88c15941c8f144f318aae Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 22 Nov 2022 20:22:41 +0800 Subject: [PATCH 0478/1723] 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 4944bca5734556331f0a12dfc1b504344adc31a9 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 22 Nov 2022 20:37:41 +0800 Subject: [PATCH 0479/1723] 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 6da7e522055fa2222554f329f21d6e0118fd4f8c Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 22 Nov 2022 20:51:21 +0800 Subject: [PATCH 0480/1723] 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 dc49c01bb55b32e59de61880073bc142c09f206a Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 23 Nov 2022 08:58:22 +0800 Subject: [PATCH 0481/1723] 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 902445b5573473969e3ebb627760fe3c4a8ca0f3 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 25 Nov 2022 15:39:14 +0800 Subject: [PATCH 0482/1723] 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 c8f225c0735b7ff7280a967023f1a487da9ba41b Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Sat, 26 Nov 2022 01:45:48 -0500 Subject: [PATCH 0483/1723] 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 8f22248e5a2cc8696b654d045fab5ed4aa7d1141 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 17 Nov 2022 15:36:30 +0800 Subject: [PATCH 0484/1723] 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 32bc000677e2169e3013a293a2b968e8a1729d2c Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 24 Nov 2022 16:06:23 +0800 Subject: [PATCH 0485/1723] 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 a760ff7d42286eae6b611ec2739fb808e8cccc31 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 24 Nov 2022 17:44:12 +0800 Subject: [PATCH 0486/1723] 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 9afb4668a744a894a93e0fe037faf70954f81b1e Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 24 Nov 2022 17:47:16 +0800 Subject: [PATCH 0487/1723] 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 d55441351bac072f22cc50398ad84ef6ec074ac4 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 26 Nov 2022 14:28:32 +0800 Subject: [PATCH 0488/1723] 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 fb44ad085ea247379f6c8f59b84a952947fb79b1 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 24 Nov 2022 18:16:40 +0800 Subject: [PATCH 0489/1723] 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 d7229049fb8c33a97279704a0b0105918b3d762a Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 26 Nov 2022 09:40:42 +0800 Subject: [PATCH 0490/1723] 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 584eaa510d1e9391be653f929e7e9130a302cf1f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 19 Nov 2022 19:32:05 +0800 Subject: [PATCH 0491/1723] 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 27772b3b1599a27ab4c9369f83eed243b736f48f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 19 Nov 2022 20:59:30 +0800 Subject: [PATCH 0492/1723] 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 da3196d858c7f1ed570dc262184dc92284b6c089 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 19 Nov 2022 21:01:21 +0800 Subject: [PATCH 0493/1723] 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 63ecd0a550b6ac2f2471b4ee3d27890805b73c9e Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 6 Nov 2022 14:48:12 +0800 Subject: [PATCH 0494/1723] 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 fc1982d0b25c4b83210a7e0c763d3b27b2187c92 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 19 Nov 2022 22:55:16 +0800 Subject: [PATCH 0495/1723] 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 9168ad013..a3bed40d1 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 13d9662ce5776329b3ab8e018f1900be94f2428f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 20 Nov 2022 00:05:00 +0800 Subject: [PATCH 0496/1723] 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 a3bed40d1..407819fde 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 df92e1417be2a46cdf3db2f57f47879c7efaa718 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 20 Nov 2022 04:45:31 +0800 Subject: [PATCH 0497/1723] 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 f6ba7a46845447d55c702b775aa6e26bf67f7a44 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 20 Nov 2022 08:34:11 +0800 Subject: [PATCH 0498/1723] 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 efb2b80f84c45661f66cea364f4dfa291e9705cd Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 20 Nov 2022 09:41:18 +0800 Subject: [PATCH 0499/1723] 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 fbf751faf89e60f72678bbed849e5333c7193574 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 20 Nov 2022 11:11:16 +0800 Subject: [PATCH 0500/1723] 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 5999fdbbd262de8d4eda375980959f26878007d5 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 23 Nov 2022 11:08:48 +0800 Subject: [PATCH 0501/1723] 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 ce9d0c3405e72c47251fcd949932842227db3ba9 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 23 Nov 2022 11:18:19 +0800 Subject: [PATCH 0502/1723] 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 7cbc06ef542e9a19ac11fc84114bb46c40f1b1c0 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 25 Nov 2022 12:46:20 +0800 Subject: [PATCH 0503/1723] 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 84bacdabbb452b8129bf915328757114859d337f Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 25 Nov 2022 12:52:10 +0800 Subject: [PATCH 0504/1723] 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 440e0463bba96020a7aecd4b6033d31d45432f78 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 27 Nov 2022 13:48:45 +0800 Subject: [PATCH 0505/1723] 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 e3032c5cea403fb6a25d6f453c28f6292e60d4ee Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 27 Nov 2022 14:13:23 +0800 Subject: [PATCH 0506/1723] 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 004fb4a315a13bf63e7fa6f34c86568c23f4925c Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 20 Nov 2022 16:31:13 +0800 Subject: [PATCH 0507/1723] 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 fff9ac40146dd895371bdc332a51f4982109a416 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 20 Nov 2022 21:00:36 +0800 Subject: [PATCH 0508/1723] 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 6b6bdde932b194e2a793bf66336634dc3dfa269e Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 28 Nov 2022 17:45:08 +0800 Subject: [PATCH 0509/1723] 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 | 1 + 1 file changed, 1 insertion(+) diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_vm/syscall.rs index d73b78b07..75f4ab3ed 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_vm/syscall.rs @@ -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 8acfb3b64c204f10c6175bb452a0cdb903da1b7b Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 28 Nov 2022 17:49:08 +0800 Subject: [PATCH 0510/1723] 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 db524374576125355d447d13013228946328b77f Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Mon, 28 Nov 2022 10:17:52 +0800 Subject: [PATCH 0511/1723] 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 352cfd052..f051cfdc3 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -37,7 +37,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 216fc0ab7..e0f63a121 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 183c9f41bf8518dbf9c9fdce8638fc956a6640d4 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 29 Nov 2022 10:32:04 +0800 Subject: [PATCH 0512/1723] 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 be760720f..774a1899d 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 35eb05ecdbc13365f04e9d15c26725e06018e073 Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Sun, 20 Nov 2022 11:31:00 +0000 Subject: [PATCH 0513/1723] 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 ca00c977a..456836934 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -331,7 +331,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() @@ -531,7 +531,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 8c74119ad07fe13a46ac45356b881b8b0e0b1d0c Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 29 Sep 2022 19:39:37 +0800 Subject: [PATCH 0514/1723] 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 52826577d5f54fdc273d859cb4190cc3d39a757f Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 6 Nov 2022 22:55:01 +0800 Subject: [PATCH 0515/1723] 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 8574bdbe191c7a54d97b1ff06b93edb397a0e740 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 9 Nov 2022 16:17:41 +0800 Subject: [PATCH 0516/1723] 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 4a4edca6a865239b169f51a361fb75cb76939831 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 28 Nov 2022 19:53:40 +0800 Subject: [PATCH 0517/1723] 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 0655b79c4fc868a09b2924aad756848f2d061eee Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 28 Nov 2022 20:11:50 +0800 Subject: [PATCH 0518/1723] 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 d5c3a81142a45f675c10405ca25e85d099e1c4cf Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 28 Nov 2022 20:24:21 +0800 Subject: [PATCH 0519/1723] 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 bdca87c93d3698cf862cb83f4959c17ceb0045f6 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 28 Nov 2022 20:28:15 +0800 Subject: [PATCH 0520/1723] 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 cd34c066773e942550ea6977c9411873380ddb37 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 28 Nov 2022 20:45:32 +0800 Subject: [PATCH 0521/1723] 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 3448f7a8defbb9ffcb016476ab57628e9060f40a Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 28 Nov 2022 21:05:41 +0800 Subject: [PATCH 0522/1723] 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 60a52bb823e2bf7d2874174c7ed7a63dcb02c69f Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 29 Nov 2022 11:36:44 +0800 Subject: [PATCH 0523/1723] 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 1f3ab9a4b9745d29d7268f91c6fa4c05c72ec6c2 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Tue, 29 Nov 2022 15:28:42 +0800 Subject: [PATCH 0524/1723] 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 c1c6ac4a6542155865fdccfd8821a46d5d8f7f42 Mon Sep 17 00:00:00 2001 From: Ikko Ashimine Date: Thu, 8 Dec 2022 10:22:13 +0000 Subject: [PATCH 0525/1723] 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 7da0eba89..baf0aa9c0 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 60b516c4d71d93fa89f9e88c0c18ca35342da45e Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 30 Nov 2022 21:11:47 +0800 Subject: [PATCH 0526/1723] 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 baf0aa9c0..34ce61120 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 aad9634bbf5831b762d309bc20e7cf52f5f33308 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sat, 26 Nov 2022 16:28:47 +0800 Subject: [PATCH 0527/1723] 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 674dad4d94c155c0df52ad19abb786f66e34abd5 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sat, 26 Nov 2022 17:45:53 +0800 Subject: [PATCH 0528/1723] 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 d4a8c8f37..cf094c4d2 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 635a67e4a089fdd344a61c9d6c23e80469d1267a Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 29 Nov 2022 18:58:04 +0800 Subject: [PATCH 0529/1723] 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 d8c0ff3487ed5961dde4624a5b5eb77bab4ca003 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 29 Nov 2022 19:30:59 +0800 Subject: [PATCH 0530/1723] 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 36c39580e97059a9ceba3e9a745fd4ec9475c989 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 15 Nov 2022 05:38:40 +0800 Subject: [PATCH 0531/1723] 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 f78758f9f6b09be3de1d0f3abebc542bea5551c7 Mon Sep 17 00:00:00 2001 From: Wu Binfeng Date: Sun, 4 Dec 2022 17:30:44 +0800 Subject: [PATCH 0532/1723] 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 e03447342223696235b422f0af885974aca7044f Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Thu, 1 Dec 2022 16:09:03 +0800 Subject: [PATCH 0533/1723] 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 a98c12b0907e58550bb016146c2e3b669c4fa0f5 Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Thu, 1 Dec 2022 18:01:43 +0800 Subject: [PATCH 0534/1723] 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 ba1c3375e975ab64fa64ff7280e149dd9390f639 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 28 Nov 2022 14:15:07 +0800 Subject: [PATCH 0535/1723] 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 e2561e9dd7f4403be9b5675cf4fe7c8f07141144 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 28 Nov 2022 14:28:15 +0800 Subject: [PATCH 0536/1723] 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 92cbb134a1a2cebbec98221da7a21e1fa7858229 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 5 Dec 2022 11:05:40 +0800 Subject: [PATCH 0537/1723] 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 be39313c15b0e0c798d9d28150df89e809fdb563 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 16 Nov 2022 20:00:07 +0800 Subject: [PATCH 0538/1723] 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 93398f7cb3ae428dd8cad77a2fb36c88930ec281 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 29 Nov 2022 21:52:53 +0800 Subject: [PATCH 0539/1723] 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 159edea25326b150c1f027f8185f4628d5538eda Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 30 Nov 2022 14:38:13 +0800 Subject: [PATCH 0540/1723] 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 ebfd86e2524221c88260fc3348a5a29d6035469e Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 3 Dec 2022 15:22:06 +0800 Subject: [PATCH 0541/1723] 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 269cfccb2e9fbe5ce133b41223bd19af45866db1 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 3 Dec 2022 15:50:35 +0800 Subject: [PATCH 0542/1723] 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 d324c0243..6b84a709b 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()); @@ -1176,8 +1187,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) { @@ -1185,9 +1197,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) { @@ -1195,6 +1204,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()); @@ -1202,20 +1220,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 fe0fe7ad5..a894bc23d 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -558,7 +558,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 cda0a72143535736db8bec09cce53241d0b632cf Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 3 Dec 2022 16:54:16 +0800 Subject: [PATCH 0543/1723] 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 6b84a709b..1f58dd1bb 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; } @@ -1322,6 +1331,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 a894bc23d..adb1888f1 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(); @@ -563,7 +568,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( @@ -575,6 +580,7 @@ pub fn vnc_display_switch(surface: &DisplaySurface) { guest_width, guest_height, ); + vnc_update_output_throttle(&client); } vnc_refresh_notify(&server); } @@ -622,7 +628,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 a08a6389dc74e00622f3c6bfedccdd9e065efc8c Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 4 Dec 2022 12:09:41 +0800 Subject: [PATCH 0544/1723] 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 1f58dd1bb..db1258d11 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 16d5095256d5480df9975f6e6274df7bee400f4e Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 6 Dec 2022 10:10:42 +0800 Subject: [PATCH 0545/1723] 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 b7af4ab26b0178e7639fa2aa611d83a0c3bdd185 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 6 Dec 2022 09:45:10 +0800 Subject: [PATCH 0546/1723] 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 6ba201500c9a404e605bd01d61b4e2fb3183b769 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 6 Dec 2022 09:48:06 +0800 Subject: [PATCH 0547/1723] 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 820de1f276850d6e5dd1786d6f349472303f03d2 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Thu, 1 Dec 2022 16:55:50 +0800 Subject: [PATCH 0548/1723] =?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 b1d8a3e5e16af8e20cd382c7da0768b7daf2b33d Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 21 Nov 2022 01:45:33 +0800 Subject: [PATCH 0549/1723] 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 33e78ba81..438aa1335 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -784,7 +784,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. @@ -795,11 +795,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 a8d21f624..ba2b8cae3 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -661,9 +661,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(()) } @@ -674,24 +676,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() @@ -710,6 +714,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 845a72d1872c56c343107239cbbb599d20de1ba1 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 21 Nov 2022 04:21:43 +0800 Subject: [PATCH 0550/1723] 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 ba2b8cae3..37c68d1ef 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -628,6 +628,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 b6ca3b63d6c4345282a581bbae8bc76626c920c9 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 4 Dec 2022 10:54:53 +0800 Subject: [PATCH 0551/1723] 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 438aa1335..073d12127 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -804,7 +804,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 15eea4f518d97e7645b9d6336d58b7ba7fd97bb1 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 5 Dec 2022 04:35:32 +0800 Subject: [PATCH 0552/1723] 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 37c68d1ef..5f94f501f 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 adb71759d..ac865b99e 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 b26f1f7764ae273f22dd39d7d50fcd1df928affd Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 5 Dec 2022 05:33:12 +0800 Subject: [PATCH 0553/1723] 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 5f94f501f..7c7ab4946 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1315,10 +1315,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 { @@ -1345,13 +1342,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))?; @@ -1371,10 +1366,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))?; @@ -1391,10 +1383,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))?; @@ -1414,15 +1403,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 { @@ -1430,21 +1417,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 a8f038cfd0adc92ecf5bb7f3d29cc565a0ffa172 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 5 Dec 2022 05:50:50 +0800 Subject: [PATCH 0554/1723] 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 7c7ab4946..d39031f86 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::{ @@ -1291,6 +1293,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 @@ -1316,13 +1386,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 { @@ -1347,9 +1423,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")] @@ -1367,9 +1447,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 8e3433009c2852e4d88bef6f2b6cf62e983463d7 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 5 Dec 2022 09:17:26 +0800 Subject: [PATCH 0555/1723] 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 d39031f86..3d25d94ed 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")?; @@ -694,6 +697,7 @@ pub trait MachineOps { let device = Arc::new(Mutex::new(ScsiDisk::ScsiDevice::new( device_cfg.clone(), scsi_type, + self.get_drive_files(), ))); let cntlr_list = self 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 ac865b99e..bef579cbb 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")?; @@ -1194,7 +1194,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, @@ -1207,6 +1207,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, @@ -1214,6 +1215,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, @@ -1226,10 +1236,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, + ) + } } } @@ -1240,7 +1254,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 f4adc16ae60c48eb108061192d0cc57bc4f2190f Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 6 Dec 2022 20:20:25 +0800 Subject: [PATCH 0556/1723] 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 2cc41d38f..30accf20c 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 a4df1e6857622438456998f67e0aa2dba723fced Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 6 Dec 2022 20:42:02 +0800 Subject: [PATCH 0557/1723] 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 073d12127..0a3cc00d3 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 7b623f86d77a5fb0c44d34d172958c4d1e634e88 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 6 Dec 2022 20:48:36 +0800 Subject: [PATCH 0558/1723] 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 64b0c8c6ef2a09a479b7a0fb3d41ed60fc3343cb Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 5 Dec 2022 22:59:57 +0800 Subject: [PATCH 0559/1723] 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 fc44e39817ae93017f64c08fb0a57e950cc5e1c2 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Tue, 6 Dec 2022 22:50:11 +0800 Subject: [PATCH 0560/1723] 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 6f73bc5d4e063ce598eae0610b5cf6860f1b9d49 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 5 Dec 2022 16:52:00 +0800 Subject: [PATCH 0561/1723] 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 a55d3416ae1b687f176eb57d12d641df35743f28 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Wed, 7 Dec 2022 11:33:49 +0800 Subject: [PATCH 0562/1723] 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 a7df5bfd6440c68f79e0ca8b4bf9ef439bd9031e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 6 Dec 2022 05:58:43 +0800 Subject: [PATCH 0563/1723] 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 0af6b36b82867ac23f104a1ccb15dc99f2cd1dcc Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 8 Dec 2022 16:26:24 +0800 Subject: [PATCH 0564/1723] 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 dcaa2aef9aa2715a57352924122c337b6ede85ce Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 8 Dec 2022 16:36:01 +0800 Subject: [PATCH 0565/1723] 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 46a603dbca464d4ae0ccac3a73356c31ccefa6eb Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 8 Dec 2022 18:43:18 +0800 Subject: [PATCH 0566/1723] 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 f8ee7d124fa709d601aad37a7ee8c0a019d2b9e2 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 6 Dec 2022 12:53:37 +0800 Subject: [PATCH 0567/1723] 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 f2fb6670f2f0812e2f3f6914736edcae0a5e696b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 6 Dec 2022 16:23:11 +0800 Subject: [PATCH 0568/1723] 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 4e1fdfe871b447550557dbe7a4e86ca6ad9ce4ba Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 6 Dec 2022 15:43:00 +0800 Subject: [PATCH 0569/1723] 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 808308bd7c57177aa6d4d638ff5ed7ba2946b1a1 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 6 Dec 2022 19:54:05 +0800 Subject: [PATCH 0570/1723] 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 70cebb08a3b80eed2a4489d13cc5c4e1cdf7a6f8 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 4 Dec 2022 20:08:37 +0800 Subject: [PATCH 0571/1723] 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 95658f56c576f2900630e326933465a99ce10e93 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Fri, 9 Dec 2022 14:54:01 +0800 Subject: [PATCH 0572/1723] 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 5920302678d4505c8923505f788ceee7c5d80e37 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 9 Dec 2022 15:12:20 +0800 Subject: [PATCH 0573/1723] 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 6383ebe7274958c5f126c65c11f59663e94fd308 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Fri, 9 Dec 2022 20:43:29 +0800 Subject: [PATCH 0574/1723] 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 aee4f4b2f295b314bdb3acc6ac66ec43bc01379d Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Sat, 10 Dec 2022 14:16:12 +0800 Subject: [PATCH 0575/1723] 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 77a6d01ebddcfbf6d6471fbe8220ffdc160c8429 Mon Sep 17 00:00:00 2001 From: Wu Binfeng Date: Mon, 12 Dec 2022 14:14:50 +0800 Subject: [PATCH 0576/1723] 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 c75528d121394a32d0ce5a37fe669c327c8b97bd Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 3 Dec 2022 15:11:44 +0800 Subject: [PATCH 0577/1723] 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 2796418a4ffed0342e47dd5d677332154cf628ac Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 3 Dec 2022 16:01:52 +0800 Subject: [PATCH 0578/1723] 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 73b48baea395ae51f766d1a119efe5ad657e1fab Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 3 Dec 2022 18:13:53 +0800 Subject: [PATCH 0579/1723] 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 941a007308f6ffb12d45f3e0b172334d97a94dd9 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 12 Dec 2022 21:29:34 +0800 Subject: [PATCH 0580/1723] 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 4310225c5..d0b23caf8 100644 --- a/migration/src/migration.rs +++ b/migration/src/migration.rs @@ -719,6 +719,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 008464fd7e44519407a02aea377c9c40894858b6 Mon Sep 17 00:00:00 2001 From: xuexiaowei1 Date: Wed, 7 Dec 2022 17:43:39 +0800 Subject: [PATCH 0581/1723] 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 eb8c05685..13c99b738 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -841,6 +841,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 @@ -1101,7 +1138,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 b8ce57350..d72651c8b 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -284,7 +284,7 @@ impl VirtioDevice for Block { Ok(()) } - /// activate device + /// Activate device. fn activate( &mut self, _mem_space: Arc, @@ -303,17 +303,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 681120098eb0b02f2e2b1a7d85d1acd48547d11c Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 13 Dec 2022 23:22:17 +0800 Subject: [PATCH 0582/1723] 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 e8aa6217daf08ef136764b5aa2ab5ed9339b3e67 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 13 Dec 2022 14:33:06 +0800 Subject: [PATCH 0583/1723] 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 ca86b5b914c103039c1dcc319077a8a4897af40e Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 29 Nov 2022 20:48:16 +0800 Subject: [PATCH 0584/1723] 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 d6ab580c7c94663f64dcf230cd9ca46c42b5e072 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 30 Nov 2022 10:37:09 +0800 Subject: [PATCH 0585/1723] 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 b01c13cd9e51e7d3d880b766199e9906e7cd39cd Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 30 Nov 2022 10:54:58 +0800 Subject: [PATCH 0586/1723] 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 5b89c7f7dddc099d8182b1f722759273ac3b482e Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 30 Nov 2022 15:07:51 +0800 Subject: [PATCH 0587/1723] 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 43e5e11a85361ed828713f95d2a6c6daf7e67a13 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 11 Dec 2022 22:54:42 +0800 Subject: [PATCH 0588/1723] 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 ade5118ffdd89e31314427f986e55bc801712b97 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 17 Dec 2022 11:54:32 +0800 Subject: [PATCH 0589/1723] 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 3a88b57ebfc814d2cfc94a201441f8150673ca36 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 17 Dec 2022 15:56:15 +0800 Subject: [PATCH 0590/1723] 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 1bc4f407821992d0aa83d25706bb829082ecab2d Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 17 Dec 2022 16:41:56 +0800 Subject: [PATCH 0591/1723] 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 cbd0bcb019cefefeb3412dc032bbd7b726f0821d Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Fri, 16 Dec 2022 15:50:17 +0800 Subject: [PATCH 0592/1723] 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 8ef47abea50aecee8fd894ed1303c3c8d525a1bc Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 19 Dec 2022 20:29:53 +0800 Subject: [PATCH 0593/1723] 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 87af1c1ae9cf82aa90d3c278697af03a94fb0849 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 19 Dec 2022 10:34:00 +0800 Subject: [PATCH 0594/1723] 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 b86b85b78..1e2311623 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -1484,6 +1484,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 625c3d0eea4bd63b159a9d01bd9617cf1a56e2bf Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 19 Dec 2022 10:36:29 +0800 Subject: [PATCH 0595/1723] 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 1e2311623..068c365ee 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; @@ -1550,7 +1553,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 43f68c3de23b0d3bb15d2285b6f6f75d6e1ee5f3 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 17 Dec 2022 19:25:13 +0800 Subject: [PATCH 0596/1723] 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 7428b7f7f012b0ad82ee031d5d20aba9aad67ae2 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 17 Dec 2022 19:26:27 +0800 Subject: [PATCH 0597/1723] 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 178760c5ad20d1baee74b984d77a9cc13a54709e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 18 Dec 2022 14:02:25 +0800 Subject: [PATCH 0598/1723] 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 7cd76c77c1d09ea6094ec35b01ee515d7d5c74cf Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 19 Dec 2022 18:13:05 +0800 Subject: [PATCH 0599/1723] 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 953a32af082f5a325e5e2ba5b7e3d17478f697f6 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 19 Dec 2022 20:00:47 +0800 Subject: [PATCH 0600/1723] 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 38715163ee2b9333093b29c7778d4d57dadc85eb Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 18 Dec 2022 11:47:06 +0800 Subject: [PATCH 0601/1723] 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 621522a280b655f9fbefdd886e691a393e94b460 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 18 Dec 2022 11:52:36 +0800 Subject: [PATCH 0602/1723] 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 ff4013fc39b509136664f55e8186f002e9948c0d Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 18 Dec 2022 11:52:56 +0800 Subject: [PATCH 0603/1723] 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 26073baae8887553d67ffb994b534c7766a17533 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 18 Dec 2022 14:45:47 +0800 Subject: [PATCH 0604/1723] 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 eca184e8abc0aab0f35891c0cf3396920368730f Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 18 Dec 2022 14:56:12 +0800 Subject: [PATCH 0605/1723] 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 c37b126ca168eb0647607236d5ef048b2249e648 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 18 Dec 2022 16:03:37 +0800 Subject: [PATCH 0606/1723] 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 fa068bffe60a7ce38482029c334b31c71b48d4d2 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 20 Dec 2022 16:38:18 +0800 Subject: [PATCH 0607/1723] 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 2a030c2c59b414d3376a21c4e350d90a2f019bea Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 20 Dec 2022 17:36:01 +0800 Subject: [PATCH 0608/1723] 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 df80c8d67..18c6d31a2 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -215,8 +215,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 f213d51ec..c441fb669 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -226,8 +226,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 7638ae984..4743923a9 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -414,7 +414,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>>) { @@ -535,10 +535,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() @@ -547,8 +543,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 { @@ -567,14 +565,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 174d3d07d6e701f7e4d92cf522aad6a3941c3e91 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 20 Dec 2022 20:20:12 +0800 Subject: [PATCH 0609/1723] 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 c081f1b7deade9c341b83d9f07ac72a3be9fd019 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 20 Dec 2022 20:34:41 +0800 Subject: [PATCH 0610/1723] 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 cfad0178a2ce69d2838b3f00be52e352b1eb41a5 Mon Sep 17 00:00:00 2001 From: Wu Binfeng Date: Tue, 20 Dec 2022 21:59:22 +0800 Subject: [PATCH 0611/1723] 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 3ccaa721288b5411d892c8f49ce4d0069b17be43 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Fri, 16 Dec 2022 15:01:49 +0800 Subject: [PATCH 0612/1723] 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 5ee520c52576b8b8c6e0990f52030c86ead83f1c Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 21 Dec 2022 10:17:51 +0800 Subject: [PATCH 0613/1723] 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 73ab5776c40170f1bbd3d6d6215f45697302deeb Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 21 Dec 2022 15:22:47 +0800 Subject: [PATCH 0614/1723] 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 f11c22ea43131af84121b0cdb3bd728c9393e592 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 21 Dec 2022 15:33:52 +0800 Subject: [PATCH 0615/1723] 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 acbe86ad9088c48851c5d122ecce2998443476a4 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 19 Dec 2022 22:23:29 +0800 Subject: [PATCH 0616/1723] 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 7c0269da2502c29ca87077d37b9f050bf511d570 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 19 Dec 2022 22:40:54 +0800 Subject: [PATCH 0617/1723] 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 8b5db8e6f2f828c9b574a220a5f767e694546c50 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 20 Dec 2022 02:06:54 +0800 Subject: [PATCH 0618/1723] 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 f4351375d4d023b7f43b07d446ed7aec32426fc2 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 20 Dec 2022 08:31:23 +0800 Subject: [PATCH 0619/1723] 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 f4eb87e49707df80a54ecd7b89e222a7cd8c97ab Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 20 Dec 2022 08:26:41 +0800 Subject: [PATCH 0620/1723] 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 33cb58db021fb0c5e468ce23aec3e036f2d6f6be Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 21 Dec 2022 16:59:54 +0800 Subject: [PATCH 0621/1723] 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 a97cd2a3526d4bf3f612d0ed9ad3b3eb2d948dee Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 21 Dec 2022 17:41:14 +0800 Subject: [PATCH 0622/1723] 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 4246eec5200d1c00190264ee481991eb81732870 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 19 Dec 2022 19:49:59 +0800 Subject: [PATCH 0623/1723] 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 bafcbcdd7e53d7a12588c1140f627350a2d52600 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 19 Dec 2022 20:24:44 +0800 Subject: [PATCH 0624/1723] 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 151d6a415f398f0a47330c505c0500da8c20b986 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 19 Dec 2022 20:32:16 +0800 Subject: [PATCH 0625/1723] 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 b9aa3d9b9c0ab5f7eb8787cf49d7e330fcd22694 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 20 Dec 2022 09:51:44 +0800 Subject: [PATCH 0626/1723] 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 84c89f903db6152bcaac94f771252245b3f64fbd Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 20 Dec 2022 11:29:00 +0800 Subject: [PATCH 0627/1723] 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 51dec4c77bd5ea037546a8be792df754e50390a0 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 20 Dec 2022 15:48:37 +0800 Subject: [PATCH 0628/1723] 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 54839b071b1b43b2c9e093e55d785e826b0a34d8 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 22 Dec 2022 09:44:14 +0800 Subject: [PATCH 0629/1723] 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 a544caaedf2b5a4f8ea1508ec3f44cbbbed58b6b Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Fri, 23 Dec 2022 15:34:24 +0800 Subject: [PATCH 0630/1723] 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 d44cdc77521fa622be34aa27d35a41e2dc09985d Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 22 Dec 2022 21:41:09 +0800 Subject: [PATCH 0631/1723] 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 efa16231ae3e8957846cf79dffb60a4bfd5cdadf Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 21 Dec 2022 06:01:11 +0800 Subject: [PATCH 0632/1723] 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 2787727043e94dcda5d7882a9e958679323cd057 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 21 Dec 2022 06:13:33 +0800 Subject: [PATCH 0633/1723] 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 63ea49889e73da86fb578fe06f7111a5a1cadc2b Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 24 Dec 2022 11:28:23 +0800 Subject: [PATCH 0634/1723] 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 bebe0ffabd109382f30a922e3493c5ebe6e546a9 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 22 Dec 2022 11:47:01 +0800 Subject: [PATCH 0635/1723] 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 8f3562b9d61537cf1724d70d99ce799d018d6455 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 22 Dec 2022 14:10:53 +0800 Subject: [PATCH 0636/1723] 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 e7e81aceae8d9e4f589cebe8bbfe4d5f284bf540 Mon Sep 17 00:00:00 2001 From: Wu Binfeng Date: Sat, 24 Dec 2022 23:17:01 +0800 Subject: [PATCH 0637/1723] 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 e663257187356ab5a2d2db651cf2ff42c11f72cf Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 21 Dec 2022 13:22:07 +0800 Subject: [PATCH 0638/1723] 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 a28bd9d17678b517e1ea3a784d51af8b63210844 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 21 Dec 2022 10:44:53 +0800 Subject: [PATCH 0639/1723] 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 17a04cc0b3a94bac9a17349a720e01201e9b70de Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 26 Dec 2022 03:38:20 +0800 Subject: [PATCH 0640/1723] 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 c86f917a080075a0a2f8e9d73b21af1b19685daa Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 26 Dec 2022 16:16:37 +0800 Subject: [PATCH 0641/1723] 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 3509df81028c0a1399fa2639fdb9026580549294 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 26 Dec 2022 19:21:31 +0800 Subject: [PATCH 0642/1723] 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 08b200401ca8a930538eae63fd88eb2ceb10c6dd Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 26 Dec 2022 16:52:47 +0800 Subject: [PATCH 0643/1723] 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 c70d4f396d74299b44f3993df72efe3efb71f438 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Fri, 23 Dec 2022 09:49:52 +0800 Subject: [PATCH 0644/1723] 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 1d17b6a7a5af6498ee9434847be99c0e8385f9f5 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 26 Dec 2022 20:16:24 +0800 Subject: [PATCH 0645/1723] 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 0c61c597d53d9645fdd99ccd3e1afe3d53838fd4 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 26 Dec 2022 20:50:42 +0800 Subject: [PATCH 0646/1723] 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 ad202e5ed85497f0e8a3027df26f30f2547ada4b Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Wed, 28 Dec 2022 09:23:17 +0800 Subject: [PATCH 0647/1723] 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 aca7e390caab64e1bab5de526f8a37a7b6a78b80 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 24 Dec 2022 15:28:46 +0800 Subject: [PATCH 0648/1723] 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 7321d42ffb4a14f31d2e4082e3e546844885fd21 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 26 Dec 2022 15:10:42 +0800 Subject: [PATCH 0649/1723] 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 01d24e70ba2d9f14b7325fe391469c8c94caffab Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Wed, 28 Dec 2022 11:39:25 +0800 Subject: [PATCH 0650/1723] 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 b7cb938328d94d5bc594770d9f2b7ac30e4f9bc1 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sat, 24 Dec 2022 18:13:31 +0800 Subject: [PATCH 0651/1723] 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 4ad5ce4d7..60ce62141 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}; @@ -736,6 +737,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")] @@ -744,81 +752,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 4a9b30c81..f2ecd7171 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 90056541ddfdfcb431c33edae9208aec83ceaabf Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 26 Dec 2022 21:12:25 +0800 Subject: [PATCH 0652/1723] 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 f4da4cfa76f6eabbf181d7fce1aa2d0d177c200c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 26 Dec 2022 20:43:55 +0800 Subject: [PATCH 0653/1723] 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 4c2f6b0505b767c6b47a91be73ba374f89736fdf Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 26 Dec 2022 22:13:54 +0800 Subject: [PATCH 0654/1723] 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 59bc30058699dabab89de5251f93bf745489fb3b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 22 Dec 2022 16:16:58 +0800 Subject: [PATCH 0655/1723] 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 2fd13dc4f64bbf0e108a0f537e3ea973e8355f15 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 22 Dec 2022 17:16:05 +0800 Subject: [PATCH 0656/1723] 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 9284c6bb4c0323dbaf62cc38fb7e82cba7854400 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 30 Dec 2022 14:49:11 +0800 Subject: [PATCH 0657/1723] 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 0f4ad9124bf44831e597ed7b949bfa8260b10e7a Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 3 Jan 2023 19:00:00 +0800 Subject: [PATCH 0658/1723] 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 534b2994e0ee653ee5569e4c2cf11fc67bf0493d Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 4 Jan 2023 11:39:58 +0800 Subject: [PATCH 0659/1723] 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 64f94a2005aa3920e7c91e534ebf1f22cc300a88 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 31 Dec 2022 23:14:24 +0800 Subject: [PATCH 0660/1723] 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 eaed46c88cd21f7d387c430bd8b60ebc77c3d03f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 31 Dec 2022 23:15:42 +0800 Subject: [PATCH 0661/1723] 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 96718926f4c6ba28ec2ced641d9d78746f160018 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 1 Jan 2023 00:58:19 +0800 Subject: [PATCH 0662/1723] 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 c8c50673b0a0d199803433c64d4d59062b323fc9 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 2 Jan 2023 17:55:10 +0800 Subject: [PATCH 0663/1723] 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 248fd6a99256051d071c3bb66c97fc27a69709e2 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 2 Jan 2023 17:01:32 +0800 Subject: [PATCH 0664/1723] 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 70ce034fe146cbeb8b00c095f9c1fb998b072a2b Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 30 Dec 2022 14:57:35 +0800 Subject: [PATCH 0665/1723] 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 30d85a766..728bcc923 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -328,8 +328,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(); @@ -348,16 +350,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); } @@ -444,7 +468,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 { @@ -453,14 +477,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)); } }; @@ -469,42 +493,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 7e4de9b84..b7e33cc35 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, @@ -535,10 +536,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 50e2164f65d56ceb7cec3886fa2003b3d0a240d9 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 5 Jan 2023 16:31:57 +0800 Subject: [PATCH 0666/1723] 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 49b64ce23bd80bef8cd4ca11be04f86d5b875bb6 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 24 Dec 2022 08:47:25 +0800 Subject: [PATCH 0667/1723] 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 777cf1d32e18200f0c3944818dfc94549d45f6c2 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 6 Jan 2023 15:57:15 +0800 Subject: [PATCH 0668/1723] 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 ae0eb04ee..1bb71e93e 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -193,7 +193,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 aa4a8dac40e7bbeb7bd4a7437d37db39b5b8393b Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 5 Jan 2023 19:31:41 +0800 Subject: [PATCH 0669/1723] 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 3e66576a7..9ca88de26 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 68b9684bcc1ce39700080fa7db900b6a2ac2d1bf Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 9 Jan 2023 14:31:15 +0800 Subject: [PATCH 0670/1723] 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 6e6db58dbd61df3aba703aaef4f0440feee2c32d Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 24 Dec 2022 14:15:06 +0800 Subject: [PATCH 0671/1723] 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 2d31c5a47fa2a3d738bb70602dcc7b50b95fae95 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 24 Dec 2022 18:02:11 +0800 Subject: [PATCH 0672/1723] 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 d65ca3e11b3e533c79e0c108860c9079c76d0e6c Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 4 Jan 2023 10:51:04 +0800 Subject: [PATCH 0673/1723] 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 eed1a363ede487325cfb3ed95e1a4c7b01f29213 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 5 Jan 2023 09:31:40 +0800 Subject: [PATCH 0674/1723] 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 10906f128e2e4cf851b71d8623765d9a67b7549d Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 5 Jan 2023 10:01:18 +0800 Subject: [PATCH 0675/1723] 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 6748501aaecaa85066ac53a12a9b973b8dcc10c6 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 5 Jan 2023 14:42:17 +0800 Subject: [PATCH 0676/1723] 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 c0752e536368bc881a8e479a1276cf8fd100cf6b Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 5 Jan 2023 23:34:39 +0800 Subject: [PATCH 0677/1723] 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 f58a9d59b95fe34927bfe25ab26d26a5bf04350c Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 6 Jan 2023 00:35:37 +0800 Subject: [PATCH 0678/1723] 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 ce03b7f8c988d83b3b381eaf6cde46fdd9c66b15 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 24 Dec 2022 19:08:02 +0800 Subject: [PATCH 0679/1723] 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 f9e10e3e52b053064c6292c065ec35eff1078774 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 10 Jan 2023 19:21:19 +0800 Subject: [PATCH 0680/1723] 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 15482c6a26405ccfe9241383bef58e2fd4f3bf64 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 10 Jan 2023 19:28:55 +0800 Subject: [PATCH 0681/1723] 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 128ddeaceaaf8f41482d11b1a9e950e2560eaedd Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 10 Jan 2023 19:35:50 +0800 Subject: [PATCH 0682/1723] 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 1f219aa5be87607f5359ace586899316e014d557 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 10 Jan 2023 20:09:30 +0800 Subject: [PATCH 0683/1723] 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 618dba24060b327a9cd8e8a577c10462df86439b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 25 Dec 2022 03:44:50 +0800 Subject: [PATCH 0684/1723] 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 67679b0a134d645db81a6af85b39562d946ac919 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 25 Dec 2022 05:30:03 +0800 Subject: [PATCH 0685/1723] 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 29123f60a0e8081dcfa9fb6fe1961c47e40ec7b4 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 12 Jan 2023 16:57:28 +0800 Subject: [PATCH 0686/1723] 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 e0a0d41d48a39a8ac27940a00920713016d37f5e Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 12 Jan 2023 17:33:10 +0800 Subject: [PATCH 0687/1723] 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 68813e42b..5439690ed 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -1614,19 +1614,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 896f91b7b2ae12ebdd05634525fbf88764cb9cd0 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 12 Jan 2023 18:00:32 +0800 Subject: [PATCH 0688/1723] 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 d695e1c08675a283733dde08ba8d05f04ceebcb2 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 12 Jan 2023 16:45:20 +0800 Subject: [PATCH 0689/1723] 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 5e2512b5a..7db6c51da 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 282cc4f25f73e31bf780b5c4bd8cdc2d13222a88 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 12 Jan 2023 17:07:38 +0800 Subject: [PATCH 0690/1723] 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 8b5c9dddcc03582b0d488ccc69fa91ee3455381a Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 12 Jan 2023 19:16:27 +0800 Subject: [PATCH 0691/1723] 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 9e4cda8395a8fad5d9a8115239b5a1be2946da32 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 12 Jan 2023 19:32:46 +0800 Subject: [PATCH 0692/1723] 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 25195ebd0e61a23f60fa27162038e034f715a475 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Fri, 6 Jan 2023 17:37:08 +0800 Subject: [PATCH 0693/1723] 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 8143b049a488f887cfdfe22dbf2bdf5fb392f041 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Fri, 6 Jan 2023 18:51:53 +0800 Subject: [PATCH 0694/1723] 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 2739283a1972077af1d28a8ba76851b531e1d740 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 14 Jan 2023 14:24:10 +0800 Subject: [PATCH 0695/1723] 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 b7fe23c5f7ebb0cd4070a9ffb8e4ea928587b4f5 Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Mon, 16 Jan 2023 01:28:29 +0000 Subject: [PATCH 0696/1723] 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 4466c94bdfc62217a3f30b828e6f491d85b93477 Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Mon, 16 Jan 2023 01:29:21 +0000 Subject: [PATCH 0697/1723] 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 c814cbbd42b0b08dafe0e3d7b224f548d05c5c19 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 11 Jan 2023 19:45:15 +0800 Subject: [PATCH 0698/1723] 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 11bd05f5795673906d9459c1b7a34ecbf49bfe35 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 12 Jan 2023 21:12:25 +0800 Subject: [PATCH 0699/1723] 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 9c77cd3d97c1456812d8430057f7df7a27052061 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 16 Jan 2023 10:45:08 +0800 Subject: [PATCH 0700/1723] 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 1657d9e21..04579a778 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 46dfa4cbd7d1a98b95d17dd9e0dd8c5eafa6e7a8 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 14 Jan 2023 01:05:52 +0800 Subject: [PATCH 0701/1723] 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 98025819953e2563823b34ee57fde4d5eb4441ef Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 17 Jan 2023 11:00:03 +0800 Subject: [PATCH 0702/1723] 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 2d9ae8278..ab3aa317d 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 444222399..c2814c676 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 2a5f1bc0373e6eb7648f5e17398ec0eaf7a3cb38 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 12 Jan 2023 10:04:06 +0800 Subject: [PATCH 0703/1723] 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 62e09b07015dc8b2582ad63d45b6b4acc887015a Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 12 Jan 2023 11:56:48 +0800 Subject: [PATCH 0704/1723] 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 6ec96a0ffc0e6360c84d596ede0dbbd40056b2c1 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 14 Jan 2023 11:35:51 +0800 Subject: [PATCH 0705/1723] 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 fcfe05f31cfcb36ce5b56d5f7586474e077dbc97 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 14 Jan 2023 11:37:37 +0800 Subject: [PATCH 0706/1723] 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 54d2bfe80e6350dbcaa9d46153673a0f3033d881 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 17 Jan 2023 19:18:07 +0800 Subject: [PATCH 0707/1723] 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 f7e6bca06eb9e0f32e0e39b6b919164e98d2a831 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 6 Jan 2023 12:09:23 +0800 Subject: [PATCH 0708/1723] 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 d2ea40708..228a9154a 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 b4dd93e71313f77ed9f965284348bcb9ffa9ba09 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 16 Jan 2023 21:34:08 +0800 Subject: [PATCH 0709/1723] 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 7579898b8..28e5c1b6d 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 80393824e..1f93dfffb 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 e057e36c000d1a052f3e81d71982c41cecf0532a Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 16 Jan 2023 21:38:32 +0800 Subject: [PATCH 0710/1723] 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 35283dd29..6e9e5754c 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 c90529175eb23d6725f667cbc03191df6911189f Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 18 Jan 2023 14:20:37 +0800 Subject: [PATCH 0711/1723] 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 d7ded25b0211e2d9c89044f1ff2eed650401997d Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 14 Jan 2023 14:00:47 +0800 Subject: [PATCH 0712/1723] 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 b535efbc46aab08083bf7d6622cb0da437daa8c7 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 14 Jan 2023 11:58:51 +0800 Subject: [PATCH 0713/1723] 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 0548eeb8ba3f87f15f9f02a093034037747e5258 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Fri, 20 Jan 2023 10:41:14 +0800 Subject: [PATCH 0714/1723] 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 994ff7008eea486d7bafffd9855c0df67ee03c6e Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 18 Jan 2023 15:54:11 +0800 Subject: [PATCH 0715/1723] 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 b8a4f8b66e76424d621c792a611ab1029a00f04a Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Fri, 20 Jan 2023 11:07:21 +0800 Subject: [PATCH 0716/1723] 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 9906a6a967cc616c827fa6b5ffdb0ebfd41d9689 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 19 Jan 2023 21:08:00 +0800 Subject: [PATCH 0717/1723] 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 bf926f44a84e2870d8ff1803081ed2766d61609f Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Wed, 18 Jan 2023 11:44:49 +0000 Subject: [PATCH 0718/1723] 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 2cd0dae79..b6d457cb5 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 @@ -1744,6 +1738,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 @@ -1778,36 +1778,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 ab3aa317d..997043b0d 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 d16ddea9febcad5579406431867a6150cbd46f84 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 19 Jan 2023 15:16:40 +0800 Subject: [PATCH 0719/1723] 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 dfc81c57d6d397b442013443249ffceb7d674053 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Fri, 20 Jan 2023 16:01:17 +0800 Subject: [PATCH 0720/1723] 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 80019b6db..22227683e 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, @@ -1558,6 +1558,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), @@ -1567,7 +1568,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(), @@ -1583,6 +1584,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); @@ -1632,9 +1634,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(()) @@ -1642,6 +1646,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 4f3bf698330fb75a187a1118b8af7c22572127ce Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Fri, 20 Jan 2023 16:47:49 +0800 Subject: [PATCH 0721/1723] 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 e8764aa2e5e56cf7c08324c9471184bc691f2ab2 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sun, 29 Jan 2023 18:40:34 +0800 Subject: [PATCH 0722/1723] 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 0d4882ba7f444869b3dabb7f604a1e9c29c0f66e Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sun, 29 Jan 2023 18:43:02 +0800 Subject: [PATCH 0723/1723] 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 6362ed22f6b1f6f9d11027be09bc5fc3abbbfdc1 Mon Sep 17 00:00:00 2001 From: xuexiaowei1 Date: Thu, 19 Jan 2023 16:13:33 +0800 Subject: [PATCH 0724/1723] 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 bb1ad9dd3..515ed5a9b 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -135,6 +135,8 @@ impl Block { protocol_features ); } + } else { + bail!("Bad spdk feature: {:#b}", features); } drop(locked_client); -- Gitee From 1f3ea9f372ef8dacff82ca35b91ad285ce064ecf Mon Sep 17 00:00:00 2001 From: xuexiaowei1 Date: Fri, 20 Jan 2023 09:45:38 +0800 Subject: [PATCH 0725/1723] 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 23b0e457b..66a9d70a7 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -371,13 +371,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) { @@ -390,7 +390,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, ) })?; @@ -401,7 +401,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, ) })?; @@ -414,14 +414,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, ) })?; @@ -430,7 +430,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 1df017d2dafb7d511cddff8db782fc6f60f7d250 Mon Sep 17 00:00:00 2001 From: xuexiaowei1 Date: Fri, 20 Jan 2023 10:33:44 +0800 Subject: [PATCH 0726/1723] 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 66a9d70a7..05c0f2f57 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 ec0ab29ecaafc8867586319b44fc95b96325a2a8 Mon Sep 17 00:00:00 2001 From: xuexiaowei1 Date: Fri, 20 Jan 2023 10:47:04 +0800 Subject: [PATCH 0727/1723] 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 05c0f2f57..23f80b4d8 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() @@ -307,16 +321,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 { @@ -339,11 +380,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, }) } @@ -368,6 +411,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() @@ -383,6 +466,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) { @@ -397,7 +490,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!( @@ -405,12 +500,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!( @@ -567,6 +664,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 3cd67064623fb5cdbf8f295a527c310dfe5448f8 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Mon, 30 Jan 2023 09:38:35 +0800 Subject: [PATCH 0728/1723] =?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 cd17a02bf5b8d79af1a27b63d286996e89332001 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 14 Jan 2023 15:03:41 +0800 Subject: [PATCH 0729/1723] 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 1cbeb224094e915c5854defd3c7ccd4549cbaf29 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 15 Jan 2023 16:55:32 +0800 Subject: [PATCH 0730/1723] 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 55799a7a702a15a74cbdeca1983a13584db999ab Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 15 Jan 2023 21:35:39 +0800 Subject: [PATCH 0731/1723] 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 fd4667f85076f79a05b2bc5c0b8f5be1034f300f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 8 Jan 2023 22:34:29 +0800 Subject: [PATCH 0732/1723] 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 430c27da45ada5844676d4e0a1e06e470f7e25b0 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 9 Jan 2023 00:19:01 +0800 Subject: [PATCH 0733/1723] 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 98a484ac6fe6eaf97e5887d93322e0b5f30724b5 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 16 Jan 2023 18:00:11 +0800 Subject: [PATCH 0734/1723] 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 633da82268dd236594ea4e162a8c8b34196c4737 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 6 Feb 2023 17:18:48 +0800 Subject: [PATCH 0735/1723] 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 35d36a0912b800487ae2b042542fc7b902d5a01a Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 3 Feb 2023 14:26:34 +0800 Subject: [PATCH 0736/1723] 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 9e62cbdf4ae52c8a47543c180ed0847b07229942 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Thu, 19 Jan 2023 06:54:54 +0000 Subject: [PATCH 0737/1723] 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 46e52c7e06b1f4888934151dacc30e4c60d7a265 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Fri, 27 Jan 2023 22:04:41 +0800 Subject: [PATCH 0738/1723] 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 61720d7d460d838d92cf85fb73272b29df3bfbf6 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Thu, 2 Feb 2023 02:23:35 +0000 Subject: [PATCH 0739/1723] 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 96e0cd8a0..6ffb7da39 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -451,3 +451,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 d503f5efd8b39f8791d74a470c6f1782c596a958 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Mon, 6 Feb 2023 23:06:42 +0800 Subject: [PATCH 0740/1723] 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 801bd6feb0d5a1a65c5ad783e7d5ac6667288de6 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 26 Jan 2023 13:37:12 +0800 Subject: [PATCH 0741/1723] 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 a8ff396d0bdf490ddb5267bc2ff91ebd5c9a276e Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 26 Jan 2023 11:39:28 +0800 Subject: [PATCH 0742/1723] 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 6cece85f5..145828fbd 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 8ee2caf6e..d5c740bdb 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -26,6 +26,8 @@ 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::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 48eb092a6800a43d0152106abcb1120c61455c31 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 31 Jan 2023 14:35:52 +0800 Subject: [PATCH 0743/1723] 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 907ac3fa093588d756aac07ce092f58a43ac40a9 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 26 Jan 2023 18:15:31 +0800 Subject: [PATCH 0744/1723] 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 94db2210be19414c08499b70f08c3bb1fbc9724d Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Thu, 9 Feb 2023 12:42:19 +0000 Subject: [PATCH 0745/1723] 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: Mon, 16 Jan 2023 22:52:24 +0800 Subject: [PATCH 0746/1723] 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 f35bf8820f6be3212a9548132c82f3dc969f8811 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 16 Jan 2023 22:59:39 +0800 Subject: [PATCH 0747/1723] 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 e10d60a718cbff607e517d37cacbc4bf031f98fd Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 16 Jan 2023 23:40:13 +0800 Subject: [PATCH 0748/1723] 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 b96759ab053233c7ae99bbcf01d193a072fd775b Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Sun, 12 Feb 2023 10:47:55 +0000 Subject: [PATCH 0749/1723] 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 f02d1178ff277c3f2842e74353f5429bb93384cd Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Mon, 13 Feb 2023 08:44:35 +0000 Subject: [PATCH 0750/1723] 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 941e352446a2c718abee0565c1ca68bebab17f71 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 14 Jan 2023 12:16:18 +0800 Subject: [PATCH 0751/1723] 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 c73ee9ce656c923a8d483c9900aa62e1c7984033 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 11 Jan 2023 21:32:03 +0800 Subject: [PATCH 0752/1723] 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 73d2b135bbfdebc91153caab636beb190a939671 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 26 Jan 2023 21:51:53 +0800 Subject: [PATCH 0753/1723] 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 0bf4acf735a3d6a33214b45e8a77cd2f8a69f4d7 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 18 Feb 2023 17:25:12 +0800 Subject: [PATCH 0754/1723] 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 656b6a0d8db3ee34149939db2035bc6e847551f7 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 17 Feb 2023 15:00:41 +0800 Subject: [PATCH 0755/1723] 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 02e32ebd1e4794f777de0641a70265d7e78a9cb7 Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Mon, 20 Feb 2023 07:10:54 +0000 Subject: [PATCH 0756/1723] 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 8fdbde4227c7dc096749c2b1935d5951c4906b78 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 17 Jan 2023 07:08:01 +0800 Subject: [PATCH 0757/1723] 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 9775c36b274cfd08c07b7791f05691e8d3a8e6c7 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 17 Jan 2023 09:56:11 +0800 Subject: [PATCH 0758/1723] 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 18b5f78d33a8b2e11617b9758e4f643f410ff8fb Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 17 Jan 2023 13:41:02 +0800 Subject: [PATCH 0759/1723] 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 0a6b358c9f0b7ba9e2ec4d9f1038b3f010d5a6d7 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 6 Feb 2023 09:44:20 +0800 Subject: [PATCH 0760/1723] 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 195b162f1884e5ccceb57fb2d2f1028a4677de9b Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sat, 18 Feb 2023 21:31:05 +0800 Subject: [PATCH 0761/1723] 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 1dccbc12ce92e63837fd20c84bd9db8970e834da Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 21 Feb 2023 14:33:44 +0800 Subject: [PATCH 0762/1723] 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 7a0397f18a638ac0fc9b8d8591c23bbd76b66045 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 17 Feb 2023 01:11:27 +0800 Subject: [PATCH 0763/1723] 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 e8eb6fe533327ba18397b2662e69293ee987bd99 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 17 Feb 2023 20:28:40 +0800 Subject: [PATCH 0764/1723] 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 d3b525e690c8a2026fe1d0caac14ecd4ed794aae Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 17 Feb 2023 23:54:34 +0800 Subject: [PATCH 0765/1723] 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 a897be9e3ef7b9e80d077e132323018918784e51 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 27 Jan 2023 09:25:10 +0800 Subject: [PATCH 0766/1723] 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 8f9df9a7ebcd1d4c36aa45c9480483f097483744 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 28 Jan 2023 01:41:00 +0800 Subject: [PATCH 0767/1723] 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 362612e4747cb8d42dc6d732522abefd6f6cf441 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 27 Jan 2023 00:21:53 +0800 Subject: [PATCH 0768/1723] 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 755c6aa39aaab88a4d92a900b559606dfbdcfa7f Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 22 Feb 2023 16:16:27 +0800 Subject: [PATCH 0769/1723] 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 6f3a0c468cd3d867ccc0f3943a086ab52f950323 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 22 Feb 2023 14:44:22 +0800 Subject: [PATCH 0770/1723] 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 af3aa9af20ee109a75137de009e62f981dc183f4 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 22 Feb 2023 18:52:16 +0800 Subject: [PATCH 0771/1723] 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 404f4d534b1f4aad9553ad2b4ca21accea718a47 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 22 Feb 2023 19:04:14 +0800 Subject: [PATCH 0772/1723] 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 2e03affcda8664defd75f741427d13e50ba30e15 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Thu, 23 Feb 2023 09:31:34 +0800 Subject: [PATCH 0773/1723] 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 86bf04e523d61accc7278430e290d809e51a4ae8 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 23 Feb 2023 09:45:52 +0800 Subject: [PATCH 0774/1723] 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 11acf20b244b12464193adb8d00c442901c9a350 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 22 Feb 2023 14:44:22 +0800 Subject: [PATCH 0775/1723] 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 1f67d68a565fa071b3012df8c635316cf9a83f12 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Thu, 23 Feb 2023 11:34:17 +0800 Subject: [PATCH 0776/1723] 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 853d1be9f368700768c93cd519abc7ee617f7301 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Thu, 23 Feb 2023 14:42:39 +0800 Subject: [PATCH 0777/1723] 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 2b70b103e21ce3708c5f151c2c4512185d4926b8 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Thu, 23 Feb 2023 14:53:36 +0800 Subject: [PATCH 0778/1723] 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 ff12a207fc5f1ced82fd85ba961e8af6d953aee2 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 18 Feb 2023 05:12:38 +0800 Subject: [PATCH 0779/1723] 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 06f8064d32918df06618eaac00c81ca046f1040b Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 23 Feb 2023 11:02:48 +0800 Subject: [PATCH 0780/1723] 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 ff6c5fb579620dbc625a98061406abef6b17a3e5 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 23 Feb 2023 11:12:24 +0800 Subject: [PATCH 0781/1723] 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 3651dcc5c2cb9bc0605121e9fad64d3c5d1de09b Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 23 Feb 2023 11:23:57 +0800 Subject: [PATCH 0782/1723] 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 186d19534ee7f57f862d7221cfed217a15f872fa Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 26 Dec 2022 18:54:41 +0800 Subject: [PATCH 0783/1723] 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 373cb52dc8a4dd69e5aa5466a9583d05dd6fd3f6 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 28 Dec 2022 10:59:52 +0800 Subject: [PATCH 0784/1723] 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 257762738..19e00b4d5 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; @@ -35,6 +36,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 83240a4491955ed424520792f287c082c6029288 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 20 Feb 2023 14:34:34 +0800 Subject: [PATCH 0785/1723] 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 1054cc30c64611bde55b5b6689f4a481da69c81a Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 20 Feb 2023 15:18:54 +0800 Subject: [PATCH 0786/1723] 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 7ed6886f1cd3a846519ef8c4ca26d8e68e86a447 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 20 Feb 2023 17:20:28 +0800 Subject: [PATCH 0787/1723] 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 ff494bda8a168f7d12af12a5ecb43fcae6ecca2a Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Thu, 12 Jan 2023 10:20:06 +0800 Subject: [PATCH 0788/1723] 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 1be1cfbb1975cd8695427f5e2868d204ce77e37a Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Wed, 22 Feb 2023 20:05:56 +0800 Subject: [PATCH 0789/1723] 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 31cf80c9d38d720557faa024ae3169459e728aba Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Wed, 22 Feb 2023 22:29:23 +0800 Subject: [PATCH 0790/1723] 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 b3e7c4bbf71c6ab1f1ef7e802510726d96eef902 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sat, 25 Feb 2023 14:29:07 +0800 Subject: [PATCH 0791/1723] 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 2646eab69a28b08f5560d79849e64dc1884eab8c Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 23 Feb 2023 14:24:13 +0800 Subject: [PATCH 0792/1723] 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 f05cd084ce43163a6ebf0fa2f4bc480c6c2928fc Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 23 Feb 2023 14:27:10 +0800 Subject: [PATCH 0793/1723] 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 324143baa837d1eeb5320363dbc9caf304309b74 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 22 Feb 2023 00:24:25 +0800 Subject: [PATCH 0794/1723] 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 a6b055cbd..3ec289c3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -987,6 +987,7 @@ dependencies = [ "pci", "thiserror", "util", + "vnc", ] [[package]] @@ -1106,27 +1107,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 9c2335073..d690644b1 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 618d93312b340a232b55c1dd32675d90e3d67d80 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 22 Feb 2023 15:37:07 +0800 Subject: [PATCH 0795/1723] 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 487fea25fc9725e178f579f81f90d386237f5ae2 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 24 Feb 2023 23:28:01 +0800 Subject: [PATCH 0796/1723] 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 3ec289c3a..fc2a5c152 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -624,6 +624,7 @@ dependencies = [ "thiserror", "util", "vmm-sys-util", + "vnc", ] [[package]] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index d690644b1..3d0f68fa9 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 e148757511db30f5667310ccbcdee89b0f786b66 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 26 Feb 2023 13:07:09 +0800 Subject: [PATCH 0797/1723] 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 bcbf9c58502dedc45ffa584d30538f1df24a0089 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 26 Feb 2023 13:26:36 +0800 Subject: [PATCH 0798/1723] 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 52a6967050d3b3c03567cc321c17615252d3e1c8 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Sun, 26 Feb 2023 14:58:46 +0800 Subject: [PATCH 0799/1723] 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 79c9b2ff66efa7c497624ea44874e80b5a90cec3 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Thu, 23 Feb 2023 21:04:13 +0800 Subject: [PATCH 0800/1723] 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 35160d87d..d8bec2bb4 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 62583e25c..ed0f589f7 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 0bd4376b5..5559d539b 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -463,7 +463,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 698a87051..2e5c7809a 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -379,6 +379,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 @@ -565,6 +573,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 f0338b35e67d9c261e1e48c9c034ca6370ca3a01 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sun, 26 Feb 2023 15:59:08 +0800 Subject: [PATCH 0801/1723] 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 987b77f2f5c9800837ae324ddda068a1cf688b6a Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Wed, 22 Feb 2023 22:45:59 +0800 Subject: [PATCH 0802/1723] 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 dc6ba568c48d0b13f706bbf8c9a4c9992b8b5c89 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sun, 26 Feb 2023 18:25:17 +0800 Subject: [PATCH 0803/1723] 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 18512f1d9156624ecfd8b6269b6f649be05bd4f0 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 26 Feb 2023 19:20:13 +0800 Subject: [PATCH 0804/1723] 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 b6ef822561a51c1e448db5d78e1705455e114c3d Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 22 Feb 2023 16:40:09 +0800 Subject: [PATCH 0805/1723] 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 ae8fd97527b4f7c79c198d39f7a43355b9c8c843 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Sun, 26 Feb 2023 21:32:11 +0800 Subject: [PATCH 0806/1723] 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 79ebc957fdd35e954ba178ce1223965660122ddf Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 27 Feb 2023 11:02:30 +0800 Subject: [PATCH 0807/1723] 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 0c0c11b3d26c590be83d24a06533223d63604ac9 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sun, 26 Feb 2023 20:55:36 +0800 Subject: [PATCH 0808/1723] 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 065df9d997c27db0068943339bb8319ab689336a Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sun, 26 Feb 2023 11:29:50 +0800 Subject: [PATCH 0809/1723] 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 c5668ea471dfa1f4b48a363600b9cadd9b1e217c Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sun, 26 Feb 2023 19:32:34 +0800 Subject: [PATCH 0810/1723] 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 41ebcf84f84172d6cbc28387c67afad08838891b Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 27 Feb 2023 14:11:46 +0800 Subject: [PATCH 0811/1723] 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 2c691ecef480db1f1cb712479effc38393c1fc4f Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 27 Feb 2023 14:13:22 +0800 Subject: [PATCH 0812/1723] 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 73b8535be605bfd824b15ee618287d78d3d77861 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Sun, 26 Feb 2023 19:32:12 +0800 Subject: [PATCH 0813/1723] 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 839a0c634f82463c6c13146f9e44f3ff224dc61a Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 18 Feb 2023 14:36:39 +0800 Subject: [PATCH 0814/1723] 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 1d54e71c36108685725ee9f55ffbc722900e5f5f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 18 Feb 2023 14:44:21 +0800 Subject: [PATCH 0815/1723] 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 cea30e331d14a53d5965566a2068407e7f65694f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 18 Feb 2023 19:46:42 +0800 Subject: [PATCH 0816/1723] 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: Mon, 27 Feb 2023 19:29:33 +0800 Subject: [PATCH 0817/1723] 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 1825f07794f58778c7722106c23767f5779be234 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 27 Feb 2023 18:04:03 +0800 Subject: [PATCH 0818/1723] 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 d8c2082e84f3ea8c3c6e616844124fdb91734d30 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Thu, 23 Feb 2023 21:06:47 +0800 Subject: [PATCH 0819/1723] 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 1cb48279c82273e1f139755f4499d9455669feb7 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 27 Feb 2023 14:49:03 +0800 Subject: [PATCH 0820/1723] 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 57c6ca9bc25f54c3d2338c37b9ecc4f7cc11346c Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Thu, 23 Feb 2023 06:31:51 +0800 Subject: [PATCH 0821/1723] 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 7c9c5e8dbcb51e92be677ba0ffaa6bd1a89ceba8 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 27 Feb 2023 15:34:42 +0800 Subject: [PATCH 0822/1723] 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 342a71f89e3d5a21f3c3d396988944b0f51e52e2 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Mon, 27 Feb 2023 22:08:56 +0800 Subject: [PATCH 0823/1723] 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 a43d87f6b0aa418ef241bf4a1c505a748a202dbd Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 23 Feb 2023 12:43:35 +0800 Subject: [PATCH 0824/1723] 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 a6df23b2bd7df84185f959c341449677588beb50 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 23 Feb 2023 10:54:13 +0800 Subject: [PATCH 0825/1723] 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 b0ca25174f246a10c07fa90d88aec36705fd7671 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 23 Feb 2023 15:16:26 +0800 Subject: [PATCH 0826/1723] 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 2810c6fd9..b9cb533a1 100644 --- a/tests/mod_test/src/libdriver/mod.rs +++ b/tests/mod_test/src/libdriver/mod.rs @@ -20,3 +20,4 @@ pub mod virtio_block; pub mod virtio_console; 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 37ec8119b2a35dd57735f436c1e735d46d06552d Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 27 Feb 2023 15:30:19 +0800 Subject: [PATCH 0827/1723] 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 fa8ef7a0cfe71509b894972def81ad9cb6ebc631 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 27 Feb 2023 23:31:31 +0800 Subject: [PATCH 0828/1723] 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 0a024c0cc..024800443 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 @@ -564,7 +544,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]); @@ -572,7 +552,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]); @@ -627,9 +606,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 637c1c0ac7ce625703b1b38488fc88310ecbb130 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 27 Feb 2023 23:40:16 +0800 Subject: [PATCH 0829/1723] 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 e63abf7526ae8f63d30addaa917e3e1f544fd2b8 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 28 Feb 2023 00:03:57 +0800 Subject: [PATCH 0830/1723] 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 76badcf9f..cfa60b4f8 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], }, } } @@ -1755,7 +1754,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; @@ -1822,7 +1821,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. @@ -1868,7 +1867,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 dd588fe18b5e2271a2d058c3399828c97d8d1046 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 28 Feb 2023 01:15:23 +0800 Subject: [PATCH 0831/1723] 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 6b20210555b42162a063e49525130b47fe36ce02 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 17 Feb 2023 09:28:20 +0800 Subject: [PATCH 0832/1723] 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 df932f65f6ac753939935ee33f11a2f40a69fccb Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 23 Feb 2023 10:33:29 +0800 Subject: [PATCH 0833/1723] 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 f2bde7844eadf958dc4e868ef510323e2dc4ba0f Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 22 Feb 2023 16:43:03 +0800 Subject: [PATCH 0834/1723] 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 ef4cb887b486d8de8757f661f78363918a6d8d64 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 23 Feb 2023 15:09:03 +0800 Subject: [PATCH 0835/1723] 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 ad250f5a0..b369e164e 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 e45773705..4388d23e1 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 2ebc442a049b0914081ce02206ed49de61b8b7c9 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 28 Feb 2023 02:47:04 +0800 Subject: [PATCH 0836/1723] 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 b369e164e..cc855a51a 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 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_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_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 63ffab0fbc18f03444ea3b42fa6bc28f1253e4a2 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 28 Feb 2023 01:55:23 +0800 Subject: [PATCH 0837/1723] 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 36fd933b4cbfc404e2a07f68231266d9705cc0a8 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 18 Feb 2023 22:58:28 +0800 Subject: [PATCH 0838/1723] 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 4bc4005b0ec9194520a1e7f9086f945ab44829d0 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 28 Feb 2023 03:45:57 +0800 Subject: [PATCH 0839/1723] 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 348b26571..844bc1ac2 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 21d441b554889d9ad6df932db89c6a16cbacac17 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 27 Feb 2023 10:35:54 +0800 Subject: [PATCH 0840/1723] 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 2b3beeebb7f51fa8bd560806b796d30291dd260b Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Tue, 28 Feb 2023 04:09:48 +0800 Subject: [PATCH 0841/1723] 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 98eff21afa6a95b89bec4486a7d2c62123ea7c9f Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 28 Feb 2023 02:11:49 +0800 Subject: [PATCH 0842/1723] 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 182062b4d330b48fef78f97fe31df4bec6d7a18f Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 28 Feb 2023 07:23:46 +0800 Subject: [PATCH 0843/1723] 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 c8f63289c2450529abd34ec9ff36f9fb303f5910 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 27 Feb 2023 23:30:37 +0800 Subject: [PATCH 0844/1723] 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 c0749d8737bce0fb2c7420c234b6ed3597d25112 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 19 Feb 2023 01:08:12 +0800 Subject: [PATCH 0845/1723] 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 e69c4ebfacd43a727b1e7d951ab4f1281b6ad35a Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 1 Mar 2023 16:31:22 +0800 Subject: [PATCH 0846/1723] 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 73923ce960c115fe570523aef0c296b2f2c9727c Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 1 Mar 2023 16:56:57 +0800 Subject: [PATCH 0847/1723] 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 4f89e99434fc8091dc4a55561e28382ffe533a92 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 28 Feb 2023 06:56:32 +0800 Subject: [PATCH 0848/1723] 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 d67b08292b96aed9c73ba3647ffa8a542f44e986 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 28 Feb 2023 09:11:12 +0800 Subject: [PATCH 0849/1723] 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 4d77d0e21f9fd58ac0e85c6bed6ac1dbe31f90c4 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 28 Feb 2023 09:33:48 +0800 Subject: [PATCH 0850/1723] 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 e44e8c3aca1fecd04a14d76ea841d6245fde82fe Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 1 Mar 2023 18:14:19 +0800 Subject: [PATCH 0851/1723] 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 0b7b69e3c9125577e5523d86996e43b584a11977 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 28 Feb 2023 22:33:46 +0800 Subject: [PATCH 0852/1723] 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 838bf29d54258e4abfae54fc41ff417712a4c7b5 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 1 Mar 2023 00:40:53 +0800 Subject: [PATCH 0853/1723] 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 c5b704d932e1c05142959d278c1c1a1bf32775d1 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 1 Mar 2023 00:39:04 +0800 Subject: [PATCH 0854/1723] 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 4907c56fee7a1425f9233e1e7f11386edadfd0b9 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 1 Mar 2023 22:13:38 +0800 Subject: [PATCH 0855/1723] 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 38c5d50c5668f6a2c03c210f2741153447abc223 Mon Sep 17 00:00:00 2001 From: Bingsong Si Date: Tue, 28 Feb 2023 10:42:02 +0800 Subject: [PATCH 0856/1723] 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 ae9b584560d2e21a5af7d409adb9f4d6366d9ee3 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Thu, 2 Mar 2023 10:27:45 +0800 Subject: [PATCH 0857/1723] 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 49bbe3e243708777b7e149757cb2c2f0a068314b Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Thu, 2 Mar 2023 10:30:25 +0800 Subject: [PATCH 0858/1723] 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 9b2bdf5680415e1e53cff8279f81322c83a51582 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Thu, 2 Mar 2023 10:31:26 +0800 Subject: [PATCH 0859/1723] 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 58bab2c98a98c97617d7b0ee12bb561051319725 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Thu, 2 Mar 2023 10:31:53 +0800 Subject: [PATCH 0860/1723] 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 64bf34510bcc6a025e0cf376a137a24668d79a8c Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Thu, 2 Mar 2023 13:04:07 +0000 Subject: [PATCH 0861/1723] 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 27e8a55ab8a32dd5679e30335c84db71255bc31e Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 28 Feb 2023 09:06:54 +0800 Subject: [PATCH 0862/1723] 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 afd8b30dd1194d4350e0afec2114eb2c900708b7 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 28 Feb 2023 10:40:22 +0800 Subject: [PATCH 0863/1723] 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 9294e8a9685bca172551051c29239de6d49e88eb Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 2 Mar 2023 03:28:20 +0800 Subject: [PATCH 0864/1723] 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 9c086b9ce3483160effbd8130061b9c214e7898f Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Fri, 3 Mar 2023 23:03:05 +0800 Subject: [PATCH 0865/1723] 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 a48555210c4e83a4d62087a3e749ae11a14b1533 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 19 Feb 2023 02:59:11 +0800 Subject: [PATCH 0866/1723] 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 6e0520d791372e6bec5ae4ea5a63ad36c7a364f4 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 4 Mar 2023 15:27:28 +0800 Subject: [PATCH 0867/1723] 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 d5042c337c50b21e7cdabb3c1a267941f9398f79 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 28 Feb 2023 18:40:25 +0800 Subject: [PATCH 0868/1723] 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 8604dc722c504d943791a2b4039308bb05cbb728 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 1 Mar 2023 09:17:04 +0800 Subject: [PATCH 0869/1723] 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 1a5f4a5333477e39029f218a5a6364cdcaea66ce Mon Sep 17 00:00:00 2001 From: mayunlong Date: Wed, 1 Mar 2023 10:37:37 +0800 Subject: [PATCH 0870/1723] 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 94145641bcf21bdb21a3362b32a805230e2a081e Mon Sep 17 00:00:00 2001 From: mayunlong Date: Wed, 1 Mar 2023 14:45:38 +0800 Subject: [PATCH 0871/1723] 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 78e574fd3106c5df26c4cbe9804b3d1bab11a676 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 6 Mar 2023 14:37:14 +0800 Subject: [PATCH 0872/1723] 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 dbd8fc664a61b198620216e17a27c7af69ce3339 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 4 Mar 2023 17:24:31 +0800 Subject: [PATCH 0873/1723] 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 de08969560769f6dd1df7e769171edc096f4dfd2 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 4 Mar 2023 17:42:42 +0800 Subject: [PATCH 0874/1723] 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 382bccb8ddb0d5ed2075f3f1ebd16dd501bb2e77 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Tue, 7 Mar 2023 22:29:40 +0800 Subject: [PATCH 0875/1723] 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 4c24a9610597b566b0161a2583b02a55b1fcc1c9 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Wed, 8 Mar 2023 11:47:49 +0800 Subject: [PATCH 0876/1723] 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 321f8f0f23cd19118fc1c216340c9739fea9de0a Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 9 Mar 2023 18:35:49 +0800 Subject: [PATCH 0877/1723] 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 fffd809b4e2a5ee29a6420203ee23c99e40473da Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 9 Mar 2023 18:38:15 +0800 Subject: [PATCH 0878/1723] 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 b0aff0b18f5adadd0e32c2a99d93d5e986190b61 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 9 Mar 2023 18:43:09 +0800 Subject: [PATCH 0879/1723] 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 aac3cbf3f4f8e5f0b7ba19246ea265c068b3fbb3 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 9 Mar 2023 20:11:55 +0800 Subject: [PATCH 0880/1723] 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 76acaca20cc93bb5779b57c7421e07e988186e47 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 10 Mar 2023 10:42:19 +0800 Subject: [PATCH 0881/1723] 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 094f0b3ab2db7379929eff9e6ec4801cbbf97f29 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 14 Mar 2023 15:46:22 +0800 Subject: [PATCH 0882/1723] 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 341dff839c4b483873fe0e721cf4af7e9cbdad3d Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 1 Mar 2023 21:39:52 +0800 Subject: [PATCH 0883/1723] 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: Fei Xu 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 c7d45598b2f4a89f63805335fb7fbd61e7b15213 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Sat, 11 Mar 2023 05:19:00 +0800 Subject: [PATCH 0884/1723] 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: Fei Xu 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 69d34b4f619fecb89eb829977555ee087eb180c2 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 1 Mar 2023 23:42:41 +0800 Subject: [PATCH 0885/1723] makefile: Complete dependencies in yum-deps Add libcap-ng-devel and cyrus-sasl-devel. Signed-off-by: Fei Xu 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 6545cfc5a32553b8ee109b5d5547cbc7c5eb648f Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 2 Mar 2023 08:35:03 +0800 Subject: [PATCH 0886/1723] virtio-blk: Adjust judgements when merge request The easily failed judgements should be put front for better performance. Signed-off-by: Fei Xu 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 68c0d541631540646aabbd3d549484026796e771 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Fri, 17 Mar 2023 12:08:16 +0800 Subject: [PATCH 0887/1723] 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: Fei Xu 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 7c6f1576c008de321199d6c516e0b40431704458 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 16 Mar 2023 21:06:41 +0800 Subject: [PATCH 0888/1723] 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: Fei Xu 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 f863fd687..81bd5bd67 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 4088e08d23ac007386f526063ac2d1058abfa728 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 16 Mar 2023 21:16:29 +0800 Subject: [PATCH 0889/1723] 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: Fei Xu 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 def42eeb3c0c8b253572b690f89d56db8b491d91 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 1 Mar 2023 21:15:33 +0800 Subject: [PATCH 0890/1723] 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: Fei Xu 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 54ceee10e8eb77cf45e097c75fc695ea8453653e Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 2 Mar 2023 03:16:49 +0800 Subject: [PATCH 0891/1723] 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: Fei Xu 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 c5d7b64ea04e05fc3b98430c2515d8c594c66e6e Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Fri, 17 Mar 2023 23:09:32 +0800 Subject: [PATCH 0892/1723] 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: Fei Xu 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 4db82e3e6681d8203c3d79a6aa2bc74124266bbc Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 1 Mar 2023 22:31:38 +0800 Subject: [PATCH 0893/1723] 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: Fei Xu 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 0424b85c9b6ac38774ac2b456a0edd1f14b5d1a7 Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Thu, 2 Mar 2023 01:09:52 +0800 Subject: [PATCH 0894/1723] 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: boby.chen 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 751a968fbf04bc4cb15f87f75980c58421f9490f Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Thu, 2 Mar 2023 01:38:00 +0800 Subject: [PATCH 0895/1723] 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: boby.chen 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 baf28f817a5135f8e3700d8619bff79bc58a23fb Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Thu, 2 Mar 2023 01:57:34 +0800 Subject: [PATCH 0896/1723] VNC: add interface of IoOperations. Abstract tls encryption io and plain io behavior into iochannel. Signed-off-by: boby.chen 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 7bf3a33745a521cde3403ab12a8aab854d7a5d92 Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Thu, 2 Mar 2023 02:35:28 +0800 Subject: [PATCH 0897/1723] 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: boby.chen 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 8f59404751c3bcca3af971bbe70d0c59bc723a58 Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Thu, 2 Mar 2023 03:53:21 +0800 Subject: [PATCH 0898/1723] VNC: delete some unused function Delete some unused function. Signed-off-by: boby.chen 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 2105c585a8050202e8805d14738f3cea5979de68 Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Thu, 2 Mar 2023 13:05:13 +0800 Subject: [PATCH 0899/1723] cargo/package: Move usb package into devices Signed-off-by: boby.chen 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 868c9d941eb5f1c9c05d89397560050ddb31ceb8 Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Thu, 2 Mar 2023 13:06:14 +0800 Subject: [PATCH 0900/1723] 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: boby.chen 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 b8e49f251f376489e04a78297b8f03aa994cc8e1 Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Fri, 24 Mar 2023 10:31:54 +0800 Subject: [PATCH 0901/1723] 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. Signed-off-by: boby.chen --- 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 a5977663bce8adb5bec86970a3f74d4792464c9e Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Thu, 23 Mar 2023 17:14:23 +0800 Subject: [PATCH 0902/1723] 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: boby.chen 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 c23bedaf10e8be54747398d3d11828102763d9fd Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Thu, 2 Mar 2023 19:21:39 +0800 Subject: [PATCH 0903/1723] UI: resove memory leak in console When closing the console, it is necessary to free the memory of the image. Signed-off-by: boby.chen 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 9509eea70ab814efa31fa6409dbc147652c025b0 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 2 Mar 2023 19:47:43 +0800 Subject: [PATCH 0904/1723] 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 6088a69135a7fc46592ecd93aecf5689adbea9a2 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Fri, 24 Mar 2023 11:12:09 +0800 Subject: [PATCH 0905/1723] 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 9e50456351f9825387d64c4ecaac00b020fd8686 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 23 Mar 2023 19:37:31 +0800 Subject: [PATCH 0906/1723] 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 a26b991d3ba701ff5aca664ef39c41775de520cf Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 23 Mar 2023 19:43:56 +0800 Subject: [PATCH 0907/1723] 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 1f3eeae0b3baa2f7182b0e8ee570a57c9f544fd3 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 21 Mar 2023 17:30:35 +0800 Subject: [PATCH 0908/1723] 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/device/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/device/balloon.rs b/virtio/src/device/balloon.rs index e009d9e04..54d3cb217 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/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 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_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 5f2220af28ab79946ef9fedab4534baee984825e Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 1 Mar 2023 17:46:51 +0800 Subject: [PATCH 0909/1723] 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 7f719b365ff2957a60561b71ca62dd8cbcfc270a Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 23 Mar 2023 22:08:34 +0800 Subject: [PATCH 0910/1723] 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 60b61aeeeefbb0eeca44d790434ea9ceb8f8d506 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sun, 19 Mar 2023 14:48:36 +0800 Subject: [PATCH 0911/1723] 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 d1f33cb5f862ae7e067e463e77fbee1f5388cab2 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 24 Mar 2023 16:27:29 +0800 Subject: [PATCH 0912/1723] 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 18cc5640b48cc36c0c538f17eed31240255b628d Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 24 Mar 2023 12:42:57 +0800 Subject: [PATCH 0913/1723] 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 9a1f5abc1cf6d4f301bcba5e92ab59f1a233808d Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 24 Mar 2023 13:15:19 +0800 Subject: [PATCH 0914/1723] 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 3bf708cf80742e94f44e7f7c94415ac395fe0c61 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 28 Mar 2023 16:00:49 +0800 Subject: [PATCH 0915/1723] 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 4eaf706778ab04733a88fceae0c6799b69f48a57 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 29 Mar 2023 11:12:02 +0800 Subject: [PATCH 0916/1723] 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 08652b56c332cb8e4f19b7c138ea93612689de9b Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 10 Mar 2023 14:27:38 +0800 Subject: [PATCH 0917/1723] 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 34a1d731ab30c033b5b020518a289ef5ed2eb265 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 19 Mar 2023 19:54:30 +0800 Subject: [PATCH 0918/1723] 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 96f066882e51e476ccdaf4660f028b79858a200c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 24 Mar 2023 07:32:00 +0800 Subject: [PATCH 0919/1723] 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 56506bf24a21384a064c19138a475ec5297d61d1 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 24 Mar 2023 20:04:42 +0800 Subject: [PATCH 0920/1723] 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 4704ab3ce..dc7a81385 100644 --- a/virtio/src/device/scsi/controller.rs +++ b/virtio/src/device/scsi/controller.rs @@ -265,7 +265,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 { @@ -273,7 +273,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, @@ -290,7 +290,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, @@ -307,13 +307,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 8405b9e819aeb2305e745561979b624e5d3ac53b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 24 Mar 2023 09:19:49 +0800 Subject: [PATCH 0921/1723] 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 dc7a81385..bc85b7106 100644 --- a/virtio/src/device/scsi/controller.rs +++ b/virtio/src/device/scsi/controller.rs @@ -24,11 +24,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}; @@ -273,41 +272,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(), @@ -645,10 +652,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 @@ -890,10 +893,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 84df32e51..b52dc143d 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -63,6 +63,8 @@ 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 @@ -436,6 +438,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 09e4c6e832c4ee77b9aa591c4e387ac321709564 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 30 Mar 2023 21:52:19 +0800 Subject: [PATCH 0922/1723] 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 698368de79777b645f7f689f3b7b48266534d54a Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 31 Mar 2023 09:51:08 +0800 Subject: [PATCH 0923/1723] 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 6db1aa79e6d359fcaaf5f13ad86c118e96c02c3e Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 31 Mar 2023 09:55:21 +0800 Subject: [PATCH 0924/1723] 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 f0d4ee17a9eca47ec1dcfa2eae1bd275c511716b Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 2 Apr 2023 14:19:15 +0800 Subject: [PATCH 0925/1723] 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 fc3b53cba3718b489a8f6ffa01a3c7ede203d3d0 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 2 Apr 2023 21:39:54 +0800 Subject: [PATCH 0926/1723] 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 1f26dae82ddff73b2e557be35c93783a9998a53e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 25 Mar 2023 01:46:03 +0800 Subject: [PATCH 0927/1723] 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 def8387d81217c4851eb35686288f852c24d68e3 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 3 Apr 2023 10:33:16 +0800 Subject: [PATCH 0928/1723] 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 d1c55a872..982329d04 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 f4c3a3a34efc6923a827985a5322767c570138de Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 3 Apr 2023 10:42:45 +0800 Subject: [PATCH 0929/1723] 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 982329d04..711050db0 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 d5a887176bf0da11c02b61c853e7679d0ad55892 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Sat, 1 Apr 2023 16:27:29 +0800 Subject: [PATCH 0930/1723] 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 5e33e1082ad7d4cff338255429722987a280bbf3 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 25 Mar 2023 04:21:04 +0800 Subject: [PATCH 0931/1723] 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 711050db0..06891b2fb 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -43,7 +43,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; const RNG_SIZE_MAX: u32 = 1 << 20; @@ -149,10 +149,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()); } @@ -368,7 +365,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(()) } @@ -561,7 +558,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); @@ -644,7 +641,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 f79e6bce6eac1171d92eb066dcaa52d493169104 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 25 Mar 2023 06:14:29 +0800 Subject: [PATCH 0932/1723] 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 e12cca108c89317e25a6559fe88f25fe7d5a25fc Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 25 Mar 2023 07:34:08 +0800 Subject: [PATCH 0933/1723] 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 e86df2ef39939cec00a606e0f5d0f223d6ac4efb Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 25 Mar 2023 08:41:28 +0800 Subject: [PATCH 0934/1723] 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 32bab506226af4d2085751a44747e9e2377b0b34 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 20 Mar 2023 17:33:49 +0800 Subject: [PATCH 0935/1723] 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 e3931ffd8..826d8f7c7 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 f1ba7c53cfe537a90d31c0771ec143f298c41f98 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Tue, 28 Mar 2023 14:43:29 +0800 Subject: [PATCH 0936/1723] 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 56c5ff59b..84fd4c841 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 65fd9414d884c2d000eac9ddb50a6dcca179f110 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Tue, 28 Mar 2023 18:56:37 +0800 Subject: [PATCH 0937/1723] 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 488818ca0..7f97a58cc 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 4c4767f82..08f1cd122 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 c18130ba9..2fc497cbc 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 03486c04ba466853483b2f0283298bcb205c566a Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Tue, 28 Mar 2023 20:24:38 +0800 Subject: [PATCH 0938/1723] 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 84fd4c841..21104ee60 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 7f97a58cc..2cb84c1f8 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 08f1cd122..c6dcfdf7e 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 2fc497cbc..3396c8108 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 c5e07282e..1994c4831 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") @@ -145,12 +148,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) @@ -179,7 +188,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 { @@ -193,7 +205,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 { @@ -211,7 +226,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 6361adf9f..c30ffaf1a 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 e0a396493c0b431f0a9ea2ea748e9b1b3ce63bfa Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 29 Mar 2023 11:43:40 +0800 Subject: [PATCH 0939/1723] 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 826d8f7c7..913d75089 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 21104ee60..c59f7db66 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 4d5cf57307722ad4f9ee7e070d5edd23b339b475 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 29 Mar 2023 13:51:19 +0800 Subject: [PATCH 0940/1723] 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 964b3fcc72af43b8408085a01713f53c8adf306a Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 29 Mar 2023 16:01:05 +0800 Subject: [PATCH 0941/1723] 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 ba822be9cc0df6500fc798f37d52c4ac12a0f9d2 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 29 Mar 2023 16:29:03 +0800 Subject: [PATCH 0942/1723] 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 baed8a07d..8205c9eed 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 c5f52340b..ed6b6aa9d 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 48e610ad58c046310b29926af02873e33635553c Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 29 Mar 2023 22:53:47 +0800 Subject: [PATCH 0943/1723] 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 dfa6a5c61ce845e1448dfe08de5b20d3dcec9c6d Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 29 Mar 2023 23:02:31 +0800 Subject: [PATCH 0944/1723] 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 f82906a6e762b01ca80281243897bf2c24ec541c Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 29 Mar 2023 23:43:01 +0800 Subject: [PATCH 0945/1723] 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 8bedc03a2af984a6797c635a9cdc329c67c1e549 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 29 Mar 2023 23:47:55 +0800 Subject: [PATCH 0946/1723] 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 4ee61c6f39bcffaab2d787955b40edfb386570fc Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 30 Mar 2023 11:53:24 +0800 Subject: [PATCH 0947/1723] 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 4793d784bb4bfc657239ba7df1b17c3625619ebc Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 30 Mar 2023 14:43:34 +0800 Subject: [PATCH 0948/1723] 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 80b33aabd49448cac065dab9518b342482a391f0 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 30 Mar 2023 14:55:01 +0800 Subject: [PATCH 0949/1723] 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 8e6115046..43fba77ae 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -770,7 +770,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. @@ -784,7 +800,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. @@ -809,7 +825,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 @@ -859,10 +875,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 @@ -873,7 +889,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. @@ -912,7 +928,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. @@ -931,7 +947,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 3f9f1ec082bd052e37312d5063be822783b4a132 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 5 Apr 2023 16:28:12 +0800 Subject: [PATCH 0950/1723] 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 cb5c7c2663044ae6f6edeba78f2668b34b474487 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 6 Apr 2023 17:33:25 +0800 Subject: [PATCH 0951/1723] 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 db78b2b46d06fd4d62e928be3371f21b63fe9e5e Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 31 Mar 2023 09:30:51 +0800 Subject: [PATCH 0952/1723] 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 37b10b1a0ae6e35940d9245108af58bf7c459139 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 31 Mar 2023 09:46:00 +0800 Subject: [PATCH 0953/1723] 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 18b3f79ae94ed26d1f579e261661e2c21ea22432 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 31 Mar 2023 10:02:46 +0800 Subject: [PATCH 0954/1723] 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 4c72ba633d750ebb860dc036d15e8000cf2d70d1 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 31 Mar 2023 10:20:22 +0800 Subject: [PATCH 0955/1723] 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 b0ac7b871e08aac2088b779ef3f80deea19ba134 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 31 Mar 2023 11:01:50 +0800 Subject: [PATCH 0956/1723] 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 159e574299d1190266c752f604af3416f34bd7c2 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 3 Apr 2023 11:54:31 +0800 Subject: [PATCH 0957/1723] 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 70fb97eaaffcddb12949bd09be2cf84e14945096 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 30 Mar 2023 17:04:29 +0800 Subject: [PATCH 0958/1723] 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 cd871d9dfe348468b518e9656963facab14bfd43 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 31 Mar 2023 14:12:14 +0800 Subject: [PATCH 0959/1723] 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 34f68a06a4b94df3967da4bf5b785a5f0a879606 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 31 Mar 2023 14:32:14 +0800 Subject: [PATCH 0960/1723] 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 685abaa083e9239a26fac2cfb2b6254b689d0501 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 3 Apr 2023 14:21:55 +0800 Subject: [PATCH 0961/1723] 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 e6a2d52b775a53ca4f21b9a5fb65f1f586689679 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 3 Apr 2023 16:03:52 +0800 Subject: [PATCH 0962/1723] 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 e9231f22bd45ec484a115e3ba676895728a2fcaf Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 3 Apr 2023 16:30:38 +0800 Subject: [PATCH 0963/1723] 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 01e268c7edd4374cfc92b40cf0c5322fbbb318d4 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 3 Apr 2023 19:54:49 +0800 Subject: [PATCH 0964/1723] 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 02d39a071ab28a94c713e3be8acc1fd8976d107b Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 4 Apr 2023 16:54:17 +0800 Subject: [PATCH 0965/1723] 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 1484d182d03634482d658e536097201494d33731 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 4 Apr 2023 16:55:23 +0800 Subject: [PATCH 0966/1723] 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 3150a17491ab587b901fccfb87262d5dadcc8ed0 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 8 Apr 2023 11:32:02 +0800 Subject: [PATCH 0967/1723] 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 319e94a54cb06996e3cfda9b67f322a519e2ed01 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sat, 8 Apr 2023 11:15:59 +0800 Subject: [PATCH 0968/1723] 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 ba6b2f3b121a52cbe7656e4cd4daa514f4f4c61f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 8 Apr 2023 18:57:59 +0800 Subject: [PATCH 0969/1723] 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 46c8f2d43771b9e88e54dec5de5d5716c2c7194c Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 8 Apr 2023 19:10:43 +0800 Subject: [PATCH 0970/1723] 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 3379f0e26908ccc182c949b6834b436a3e3b7494 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 10 Apr 2023 10:29:53 +0800 Subject: [PATCH 0971/1723] 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 1ae47c79a68690f831740626ffb37ac9913f6113 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 11 Apr 2023 14:13:13 +0800 Subject: [PATCH 0972/1723] 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 d6c4b5659e3aaa9ff8d08ac79d970c69e56e70b0 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Mon, 10 Apr 2023 21:45:30 +0800 Subject: [PATCH 0973/1723] 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 1d9e0615d61e24f188f16ea22ebd2a5df13fd2a7 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 12 Apr 2023 11:27:12 +0800 Subject: [PATCH 0974/1723] 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 1cb575258698131197ac5ad9d9073203ad3bad3d Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 12 Apr 2023 15:15:10 +0800 Subject: [PATCH 0975/1723] 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 fd8b4b58d5d129aef0e4782b9d6c1ed34ee89521 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 7 Apr 2023 17:13:04 +0800 Subject: [PATCH 0976/1723] 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 8f2eac751a272d2134399268025b2ab7ccf71988 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 10 Apr 2023 01:36:20 +0800 Subject: [PATCH 0977/1723] 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 027c3d4dcd90b652c7f2a09ec584fb23bb2be73a Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 10 Apr 2023 03:02:24 +0800 Subject: [PATCH 0978/1723] 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 c0bf05a376b66cba45b473ca6f8c332872133739 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 10 Apr 2023 03:05:58 +0800 Subject: [PATCH 0979/1723] 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 c8cd35f7e6763af9e4c1a91adf3b374c5543110e Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 10 Apr 2023 03:11:14 +0800 Subject: [PATCH 0980/1723] 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 53157aa8860203ae4dd40492e3524eced48e41b8 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 10 Apr 2023 00:11:42 +0800 Subject: [PATCH 0981/1723] 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 c615ed37d790e91c38eb7ea58192ea5892965341 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 6 Apr 2023 22:02:00 +0800 Subject: [PATCH 0982/1723] 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 d86e5cbb0..5eaa61f72 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -542,7 +542,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 { @@ -1814,19 +1814,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 c362c90c9e9af5c1792f705df416a638add6cd07 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sat, 8 Apr 2023 21:44:33 +0800 Subject: [PATCH 0983/1723] 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 52a9f54027f6fca17b53b78a806e2d80ccb0c5c5 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sat, 25 Mar 2023 14:39:32 +0800 Subject: [PATCH 0984/1723] 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 9f387e300b4eba3f7d09f68975a5dbf0b9cd925c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 12 Apr 2023 08:38:46 +0800 Subject: [PATCH 0985/1723] 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 02c821bbd53a164c129bbd71aff16669dca87908 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Thu, 13 Apr 2023 11:25:29 +0800 Subject: [PATCH 0986/1723] 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 b126e2c9109e935f7c080c9704799d34df02d116 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 27 Mar 2023 22:00:02 +0800 Subject: [PATCH 0987/1723] 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 4164f19f9c89359df5a956d229afbe266eb563a6 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 27 Mar 2023 21:50:55 +0800 Subject: [PATCH 0988/1723] 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 31f91771221a6ac6afd5910c4e3135acfee04c00 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 27 Mar 2023 22:00:46 +0800 Subject: [PATCH 0989/1723] 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 4e1817bf413fc71b9d513da24b1f39e85888e8e1 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 28 Mar 2023 09:29:54 +0800 Subject: [PATCH 0990/1723] 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 ab9b9fe1aeebf96ce56b7a447dbe21ad5b10ef4d Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 28 Mar 2023 10:58:11 +0800 Subject: [PATCH 0991/1723] 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 759686ca36fcec3ac792a34a47269c99619e0928 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 28 Mar 2023 11:04:55 +0800 Subject: [PATCH 0992/1723] 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 a3dc34c8425a0f9a7cd8fdc67efb29456014ac85 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 29 Mar 2023 15:50:28 +0800 Subject: [PATCH 0993/1723] 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 7a5c63a80efe1d2fce84cc38340a0570a84d685d Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 28 Mar 2023 11:01:43 +0800 Subject: [PATCH 0994/1723] 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 71b3c1e72227df87b57ecfcb464c84e6693b5a0f Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 8 Apr 2023 20:40:07 +0800 Subject: [PATCH 0995/1723] 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 aa93c5199e160c9304630c577c1f2c8c85cad787 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 7 Mar 2023 00:33:52 +0800 Subject: [PATCH 0996/1723] 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 a05f870acd8300caaecefa7b5572f6ca61353abf Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 15 Apr 2023 19:18:46 +0800 Subject: [PATCH 0997/1723] 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 3d97d7515d861a6b9c6441d7293318d373c2eff6 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 15 Apr 2023 17:41:06 +0800 Subject: [PATCH 0998/1723] 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 14c2ec8e7308cb02d78d9671c151fd9c34837128 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 4 Mar 2023 10:49:09 +0800 Subject: [PATCH 0999/1723] =?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 deec58e524a93a15227839dbf8f13a043d2097b2 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 6 Mar 2023 22:20:33 +0800 Subject: [PATCH 1000/1723] 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 f8dc1b785fbe8f19918f0c97800e12deecf5a639 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 7 Mar 2023 00:06:01 +0800 Subject: [PATCH 1001/1723] 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 ee74419b4927db49c1cd40364f28f153e90d9109 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Wed, 19 Apr 2023 11:19:03 +0800 Subject: [PATCH 1002/1723] 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 a6ea2770f02c6f3b710200dfd6f717734891d367 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 17 Apr 2023 21:36:19 +0800 Subject: [PATCH 1003/1723] 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 b7954b776..c6cb47c42 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -582,6 +582,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 bc6459af2..6c630d0f0 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 095c0b918d4261866f659e2ddbbc2441ad26092e Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Sat, 15 Apr 2023 22:21:27 +0800 Subject: [PATCH 1004/1723] aio: Fix handle_misaligned_rw() Under specific situation, the tail block is not loaded, which will make disk corrupted, fix it. Signed-off-by: boby.chen --- 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 c4ef7d86579ac4e1721861a7fcacbb66ebc044c2 Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Fri, 21 Apr 2023 09:40:05 +0800 Subject: [PATCH 1005/1723] usb: ignore unimportant logs Ignore the error logs printed when the devices is not activated. Signed-off-by: boby.chen --- 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 16568be7e2a8d712071170b71846d1b15be258ab Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Wed, 19 Apr 2023 22:30:37 +0800 Subject: [PATCH 1006/1723] virtio-gpu: fix memory leak bug Fixed a bug where images that were not on the screen were not released correctly. Signed-off-by: boby.chen --- 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 1815ccae2dc933969c4b08b41b94d20fdf449213 Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Fri, 14 Apr 2023 23:36:25 +0800 Subject: [PATCH 1007/1723] Gtk: add syscall white table Add syscall white table for gtk Signed-off-by: boby.chen --- 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 99546f2d1c611df20fba29f3443cfca7bd267acb Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Fri, 14 Apr 2023 21:45:22 +0800 Subject: [PATCH 1008/1723] GTK: gtk init GTK initialization, you can get a displayable window by cmdline "-display gtk". Signed-off-by: boby.chen --- 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 411ed969ed77730bdb715c7882b817d77cf4962f Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Fri, 14 Apr 2023 22:55:01 +0800 Subject: [PATCH 1009/1723] 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: boby.chen --- 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 470cf0e1a5b5607f4a25d0eac00a07003048eb89 Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Sun, 16 Apr 2023 02:23:01 +0800 Subject: [PATCH 1010/1723] GTK: improve the performance of gtk. Reduce one memory copy during GTK operation to improve performance Signed-off-by: boby.chen --- 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 45957c6e7b886d1eaaf0a915015650ac30ff09b2 Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Sat, 22 Apr 2023 21:08:49 +0800 Subject: [PATCH 1011/1723] 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: boby.chen --- 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 47f34c12ccaabd4c41112aeb67a2af6122b113b9 Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Sat, 22 Apr 2023 15:58:40 +0800 Subject: [PATCH 1012/1723] 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: boby.chen --- 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 579fa67b8..e7077d14d 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 26342dded..00a5a7743 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -56,8 +56,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::{ @@ -669,12 +669,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 e2c53884b..58a1e656d 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -26,6 +26,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::*; @@ -50,6 +51,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 836e16122..dc3e2e380 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -22,6 +22,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}; @@ -49,8 +50,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, @@ -299,7 +298,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 878db231b..1397eef23 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 163da74a02a2723132b3c2e48ac8d26e6327c8c3 Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Sun, 16 Apr 2023 18:02:39 +0800 Subject: [PATCH 1013/1723] aio: Add UT for sync io This has good coverage for sync IO, but does not cover async IO. Signed-off-by: boby.chen --- 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 55ee2ad82d34882bf62b050a9ecd1aafa8d42d39 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 26 Apr 2023 16:52:43 +0800 Subject: [PATCH 1014/1723] 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 0b0695871de2219dce9464ecb6432d1ababfbe53 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sun, 23 Apr 2023 20:16:35 +0800 Subject: [PATCH 1015/1723] 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 ecffedf888979973d7c4c7bc6a99dd172f5c53ab Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 3 Apr 2023 21:08:24 +0800 Subject: [PATCH 1016/1723] 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 c6684c6559154b02fce6163070500577b647247a Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 10 Apr 2023 22:13:54 +0800 Subject: [PATCH 1017/1723] 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 36eff57bd4296f33f0a698ba8b25e9c75e6b358a Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sun, 16 Apr 2023 21:33:40 +0800 Subject: [PATCH 1018/1723] 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 7eca39c5823f312d4437241f00ef3de63217ec08 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 10 Apr 2023 18:11:02 +0800 Subject: [PATCH 1019/1723] 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 a40d91e8888bb89e71bd30b0ba668e31af7f0709 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 10 Apr 2023 19:19:29 +0800 Subject: [PATCH 1020/1723] 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 c1f22c90e72df82ca0b237b23e87294d7c1258ae Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 10 Apr 2023 22:29:45 +0800 Subject: [PATCH 1021/1723] 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 67041c907f3f36292c0c5d863f24e0972a2b35b2 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 10 Apr 2023 22:26:31 +0800 Subject: [PATCH 1022/1723] 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 773918bee5b47c1baf1a56aa95dfeb2f766833ac Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 21 Apr 2023 09:28:57 +0800 Subject: [PATCH 1023/1723] 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 4dc0111edf86fe6c18e9c2dcd2cf225b308930ae Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 16 Apr 2023 18:57:15 +0800 Subject: [PATCH 1024/1723] 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 79312661e86de7d413e8acef41fe5c8559395db2 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 16 Apr 2023 20:56:53 +0800 Subject: [PATCH 1025/1723] 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 a3dc668081085af9759f210c54ed6b9558d1a75f Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Thu, 4 May 2023 21:22:56 +0800 Subject: [PATCH 1026/1723] 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 7c9c63369ce575bc71002ec3c265f847ae8f6714 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 10 Apr 2023 09:31:02 +0800 Subject: [PATCH 1027/1723] 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 86a45d35e7121510ad166dd59f3adeee244bbb26 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sun, 23 Apr 2023 09:58:42 +0800 Subject: [PATCH 1028/1723] 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 a0efbcc64ca5bf10e0669486b62cf9d54565b9e2 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 24 Apr 2023 14:45:55 +0800 Subject: [PATCH 1029/1723] 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 c22310c7ca06258e17a9335232edb23ede34ce97 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 26 Apr 2023 22:35:53 +0800 Subject: [PATCH 1030/1723] 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 8e7bdfccdc92a1dd87f8b429a689019fa09ac6ec Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 17 Apr 2023 01:11:12 +0800 Subject: [PATCH 1031/1723] 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 99336fe84df0fddf06fa4dac784d1d7500033214 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Mon, 24 Apr 2023 17:40:46 +0800 Subject: [PATCH 1032/1723] 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 ba18026b48cd56f21560262fa60891b0b433d680 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Thu, 27 Apr 2023 22:49:05 +0800 Subject: [PATCH 1033/1723] 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 8890ce834775c01cb853335be94f01e51aed6cc7 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 5 May 2023 15:37:50 +0800 Subject: [PATCH 1034/1723] 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 15811409ebb2dee4547623b036d9de076a5c809e Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 17 Apr 2023 02:23:54 +0800 Subject: [PATCH 1035/1723] 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 e8c73dede..175fa736a 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; @@ -536,9 +540,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) } @@ -564,10 +579,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 95a5f1cefe7ebad9f99695c03467c182f7acdb59 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 5 May 2023 06:23:12 +0800 Subject: [PATCH 1036/1723] 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 5a922b4e159e4069e876addd8df375ba1b247875 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 25 Apr 2023 14:55:36 +0800 Subject: [PATCH 1037/1723] 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 26c4160026b51aeeab6f96d5fc5474af2863861b Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 25 Apr 2023 15:56:57 +0800 Subject: [PATCH 1038/1723] 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 b47b6f233e26372e1c691d39dfcc07cea4e5b411 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 25 Apr 2023 17:18:58 +0800 Subject: [PATCH 1039/1723] 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 16277be2f18eebc369dc90ef2e24a423e4077781 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 25 Apr 2023 17:35:50 +0800 Subject: [PATCH 1040/1723] 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 4f7e6363132557fcc38518c531b9c0644918cf64 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 25 Apr 2023 17:44:08 +0800 Subject: [PATCH 1041/1723] 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 4740ce20a1214d7a46700592bca3c59a5c53c489 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 25 Apr 2023 19:26:29 +0800 Subject: [PATCH 1042/1723] 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 b1b89b8932c56aa6b3f1168608c412c8113afaca Mon Sep 17 00:00:00 2001 From: mayunlong Date: Fri, 28 Apr 2023 17:30:16 +0800 Subject: [PATCH 1043/1723] 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 d2b0d10fb707308cb47b684b82f41f46ef01510e Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 16 Apr 2023 14:57:49 +0800 Subject: [PATCH 1044/1723] 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 43be62ae6..fac155498 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}; @@ -1330,7 +1330,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); @@ -1709,6 +1721,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 3e47054eb7226256232ad5047a20941b3fc30e65 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 17 Apr 2023 11:52:15 +0800 Subject: [PATCH 1045/1723] 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 9a8b0d750..f48003a50 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; @@ -271,8 +273,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); @@ -551,16 +553,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 21947b3f4dcba9dbca12e344ab4a4a93430622eb Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 8 May 2023 08:51:46 +0800 Subject: [PATCH 1046/1723] 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 601949a39..b77632087 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}, @@ -224,8 +224,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>, @@ -235,8 +234,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 de35c83571a0c2d6d199f1b2bbb2977b9764e185 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 9 May 2023 15:07:17 +0800 Subject: [PATCH 1047/1723] 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 e984f70c1840625a11e478103b44b06ca017f6d3 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 17 Apr 2023 13:14:41 +0800 Subject: [PATCH 1048/1723] 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 e5b3b50e759d613c9712329e273664aedb508ca3 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 8 May 2023 17:23:32 +0800 Subject: [PATCH 1049/1723] 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 34e799c60..df29d5620 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 621e45931..b63530732 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 c6f97ce2d966d68f8eefef9d37d5d4eb80513d65 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 17 Apr 2023 16:43:17 +0800 Subject: [PATCH 1050/1723] 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 3a9d8437bf68a4b3c04d50368e0dd34639a9c9d9 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 17 Apr 2023 15:52:29 +0800 Subject: [PATCH 1051/1723] 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 0895e8c78f3f4c977ceb2d103ad7458c8c7ef338 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 8 May 2023 21:23:52 +0800 Subject: [PATCH 1052/1723] 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 4c99bd807335969f099bec45c0a48abbfc540957 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 4 May 2023 11:26:06 +0800 Subject: [PATCH 1053/1723] 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 0fe623c664d8aeea50bcb6829df60b617e201fd2 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 6 May 2023 08:16:49 +0800 Subject: [PATCH 1054/1723] 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 19f3e4daf7eb245e155e5fafa642f97e1d982316 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 8 May 2023 14:47:31 +0800 Subject: [PATCH 1055/1723] 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 997a61f346dead0fff09f3a2b06afa98c22c513c Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 8 May 2023 16:39:17 +0800 Subject: [PATCH 1056/1723] 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 b6fdfaa7efc2697f23c4ad3695bd25d4e5cd03e1 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 8 May 2023 19:04:41 +0800 Subject: [PATCH 1057/1723] 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 6b8510a2df86bbc24ed3792dbcb7ba1f77eeab27 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 8 May 2023 19:55:20 +0800 Subject: [PATCH 1058/1723] 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 4c327e24c0311ac73eb512998a8cb409763ba8b5 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 8 May 2023 20:20:56 +0800 Subject: [PATCH 1059/1723] 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 5f784353dfa4df50a217af699f8f091b356f65b6 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 10 May 2023 09:45:05 +0800 Subject: [PATCH 1060/1723] 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 e2ba469cf30ed5a5925a954fbc8936d1cf25c281 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 10 May 2023 11:30:34 +0800 Subject: [PATCH 1061/1723] 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 e82a3b3a56d326275796322bc30d16e4a639a46b Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Wed, 10 May 2023 17:02:30 +0800 Subject: [PATCH 1062/1723] 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 bdcaf90f7..b53a7f15c 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}; @@ -1072,11 +1072,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 ceb6d93e7d8e072c2418bd9fa5547cf6364dbdbf Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 3 May 2023 15:13:58 +0800 Subject: [PATCH 1063/1723] 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 519c6540e44db959cf4d263aef78281d7dab3484 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 11 May 2023 11:04:49 +0800 Subject: [PATCH 1064/1723] 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 57eed4e4df3142d13b8c3dba2de3d5e02db80105 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 11 May 2023 16:37:44 +0800 Subject: [PATCH 1065/1723] 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 5cabdbe499bace0a55438901e3f1d9570f9b91ce Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 9 May 2023 04:38:52 +0800 Subject: [PATCH 1066/1723] 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 38c448763..a6c995919 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1909,7 +1909,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 c183f7b742e7a2ac4a8d88e5d4886242ac69a189 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Tue, 9 May 2023 22:04:20 +0800 Subject: [PATCH 1067/1723] 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 e2139a8d19cbe00571ff67f869c37a30d56dd34b Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Tue, 9 May 2023 22:41:39 +0800 Subject: [PATCH 1068/1723] 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 302520b4306aa5022efec313406969eaf94ecd1b Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Tue, 9 May 2023 23:15:03 +0800 Subject: [PATCH 1069/1723] 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 98eba620a66a2bec4ed42cf9b7cc939525c41829 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Thu, 11 May 2023 14:41:24 +0800 Subject: [PATCH 1070/1723] 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 55fe58ca57936086305649ff99a6a3b7ca92cc1b Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Tue, 9 May 2023 23:48:25 +0800 Subject: [PATCH 1071/1723] 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 dcccbe4da861f168513abf4f476bc7193392e842 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 10 May 2023 14:58:01 +0800 Subject: [PATCH 1072/1723] 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 f713186abc2f14d193e1f199c471f33e1e8a4ad3 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 11 May 2023 20:24:48 +0800 Subject: [PATCH 1073/1723] 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 d3baa5b3b8517c8955726138e7bdd76006f78da4 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 11 May 2023 21:32:31 +0800 Subject: [PATCH 1074/1723] 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 fcba220f942e1935de886fdc528200bca7def379 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 11 May 2023 21:21:42 +0800 Subject: [PATCH 1075/1723] 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 7fb206e84088ad142d9abc16c91993dbf0992b9d Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 14 May 2023 18:07:00 +0800 Subject: [PATCH 1076/1723] 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 2a629501a99301dc08eb17f62bd97f6eab93d146 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 14 May 2023 16:57:50 +0800 Subject: [PATCH 1077/1723] 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 1cbaca35728b494dbba4fdd87821ed28413a7335 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 14 May 2023 17:29:18 +0800 Subject: [PATCH 1078/1723] 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 cb7bf0df27d7c0852143358c9d9cbfbacf2100ed Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 11 May 2023 22:16:22 +0800 Subject: [PATCH 1079/1723] 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 8d54e3c43815e3ed7ae22cf42fab85e24175b070 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 15 May 2023 19:17:24 +0800 Subject: [PATCH 1080/1723] 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 aae9f2c77177675c5bc4e789c824cdc16227756f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 12 May 2023 07:32:24 +0800 Subject: [PATCH 1081/1723] 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 1504a567e78e0dafacf7a77a438e8787c56f0f7a Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 16 May 2023 19:08:46 +0800 Subject: [PATCH 1082/1723] 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 a1544c5bc800a7f554bac39bcbd1bdfe5121ff3b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 17 May 2023 18:59:06 +0800 Subject: [PATCH 1083/1723] 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 1bb8eabaa0ec582e69c4486ae02e7ef1ecf1620b Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 12 May 2023 06:45:59 +0800 Subject: [PATCH 1084/1723] 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 0e93d4802e7b8e4d5fdee392f4c592baef17969b Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 12 May 2023 14:29:36 +0800 Subject: [PATCH 1085/1723] 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 a91471a00ea2d09d359c60bca630114139c13c94 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 May 2023 06:36:27 +0800 Subject: [PATCH 1086/1723] 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 e96cd883a63cab265c2e586bb225de5dc4daa005 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 May 2023 06:46:39 +0800 Subject: [PATCH 1087/1723] 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 74102c527d0c1be29a7211d5da3bbb4ff0f90188 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 May 2023 06:47:03 +0800 Subject: [PATCH 1088/1723] 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 4cf8600b5..58f4beeba 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -263,15 +263,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 209f171c8bad0c492b6c854cb44ce7a3fe4f60f6 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 18 May 2023 00:06:02 +0800 Subject: [PATCH 1089/1723] 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 ec39e32e2bb48564d296d7052f2540b9be2b6590 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 18 May 2023 21:12:49 +0800 Subject: [PATCH 1090/1723] 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 be87ca62e98958800b58c21b04f32eb7652525ac Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 May 2023 11:19:51 +0800 Subject: [PATCH 1091/1723] 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 2dbffd2e5f4211111257aa62e370e6bd87015020 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Mon, 22 May 2023 13:47:18 +0800 Subject: [PATCH 1092/1723] 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 41a505b0ca4b6e38ebf1a42cea5b87dad497ccbb Mon Sep 17 00:00:00 2001 From: mayunlong Date: Mon, 22 May 2023 17:05:35 +0800 Subject: [PATCH 1093/1723] 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 5792a4ac084aeaa5796cf342be978ec9cd463412 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Mon, 22 May 2023 20:56:23 +0800 Subject: [PATCH 1094/1723] 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 44c043cb4a52b169a49dfc1a87a2385b0ba3d119 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 10 May 2023 10:15:50 +0800 Subject: [PATCH 1095/1723] 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 dbd8c8257c6721045cce731174c3a621e19f1fdc Mon Sep 17 00:00:00 2001 From: liuzitan Date: Wed, 10 May 2023 10:20:49 +0800 Subject: [PATCH 1096/1723] camera: add demo camera backend Add Demo camera backend which is used in MST. Signed-off-by: liuzitan --- 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 9f792536c5e9fc67b31deab0b03de8768c9f0773 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 26 Apr 2023 22:37:37 +0800 Subject: [PATCH 1097/1723] 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 ca1c54d1d0836e4622da706c490f30ec6f97aae5 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 10 May 2023 10:21:44 +0800 Subject: [PATCH 1098/1723] 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 2a592c333216a93eb5416367e7581d13d19de65a Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 May 2023 13:55:20 +0800 Subject: [PATCH 1099/1723] 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 f1d79d16c129ff37d71249a3e5e19d906f6e2631 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 May 2023 15:00:02 +0800 Subject: [PATCH 1100/1723] 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 e5568af1b58cb6a83b604eaaf66e6a6d6cbbe9d6 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 May 2023 16:46:53 +0800 Subject: [PATCH 1101/1723] 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 f07533d38..fdc5af243 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -627,8 +627,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. @@ -962,19 +961,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 dbea8ce92a229a78f9359f20694fd9d8172de6c9 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 16 May 2023 17:10:27 +0800 Subject: [PATCH 1102/1723] 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 68a6fff28..611c3a695 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1003,6 +1003,7 @@ dependencies = [ "pci", "serde", "serde_json", + "smbios", "strum 0.24.1", "strum_macros 0.24.3", "sysbus", @@ -1618,6 +1619,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 36a488666..029a2ec50 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 0f0587ede9ce0a7d930a98428dbaeb7b3d284239 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 30 May 2023 15:23:43 +0800 Subject: [PATCH 1103/1723] 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 31c8a3fe1ecd1c9cde2f207cf39c5554fc2f36e1 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 25 May 2023 23:35:14 +0800 Subject: [PATCH 1104/1723] 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 1ee2c18dfe49a0e5c6ad7cc4bf049649794ee2bc Mon Sep 17 00:00:00 2001 From: Dmitry Skorodumov Date: Mon, 29 May 2023 14:44:14 +0800 Subject: [PATCH 1105/1723] 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 005ba0bcf655af45301ffe9a6692a82bc33d386c Mon Sep 17 00:00:00 2001 From: Dmitry Skorodumov Date: Mon, 29 May 2023 14:49:38 +0800 Subject: [PATCH 1106/1723] 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 f12485854d5c52fc7527f8dbf99675e8c4c58b01 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 30 May 2023 20:05:02 +0800 Subject: [PATCH 1107/1723] 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 035505a04ddbf2d688e4c0a8af341c93b6d16e4d Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 5 Jun 2023 16:14:45 +0800 Subject: [PATCH 1108/1723] 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 58013096184c4517b471b01d64fe2e7ea84eceae Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 31 May 2023 16:08:09 +0800 Subject: [PATCH 1109/1723] 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 b090e5eac99bdebfc93d97ffeb3dfd5a753e037d Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 30 May 2023 12:06:19 +0800 Subject: [PATCH 1110/1723] 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 9a850d1dd6aa0f2d5bdec25200eb44e82f894b6f Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 30 May 2023 12:11:31 +0800 Subject: [PATCH 1111/1723] 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 8786f2419..c29151ac5 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", @@ -1116,6 +1139,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 94ef83ecfbad30eeb42e2f983d5772393cba07fe Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 2 Jun 2023 15:22:37 +0800 Subject: [PATCH 1112/1723] 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 5769114a0736caf34531a1c0e3e8c96e0dd5b94e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 6 Jun 2023 20:48:14 +0800 Subject: [PATCH 1113/1723] 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 2981d5b43efb0c9c5f107d2c22873d7ceff34f56 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Thu, 8 Jun 2023 13:08:56 +0800 Subject: [PATCH 1114/1723] 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 0ce5b8d2df6d19be7f6f3b69979c449abbbecbe7 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 16 May 2023 19:54:02 +0800 Subject: [PATCH 1115/1723] 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 5613c4bc1935edbcb1b2de07aaf7c4f29ed9da42 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 8 Jun 2023 20:26:07 +0800 Subject: [PATCH 1116/1723] 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 3ebc57b66f96f445cc52211c6be5354af4742c7b Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 16 May 2023 17:15:38 +0800 Subject: [PATCH 1117/1723] 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 4bfc68ef24d30b416cbda06783838558d27a1f31 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 20 May 2023 18:57:58 +0800 Subject: [PATCH 1118/1723] 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 3efbef74534a6ce3916a4508028e45ba526d1715 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 20 May 2023 19:11:29 +0800 Subject: [PATCH 1119/1723] 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 a114370467a8969d60d9c127cf886366f0459680 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 20 May 2023 19:39:40 +0800 Subject: [PATCH 1120/1723] 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 6ebac3dda81930a959e345ffedc7de85fa6c692b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 21 May 2023 02:34:05 +0800 Subject: [PATCH 1121/1723] 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 400b1aad2f04aa6177b93c470d6fb2e7df8d028b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 20 May 2023 22:50:23 +0800 Subject: [PATCH 1122/1723] 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 75aec6d694db5863f401c0fb273a44a592bca8b5 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 20 May 2023 23:16:10 +0800 Subject: [PATCH 1123/1723] 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 59981e3d86e7aae1d077081aab8a70b1e1f654f7 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 20 May 2023 23:55:48 +0800 Subject: [PATCH 1124/1723] 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 da41837c4826509c02132365fa4c8c372b3efc41 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 21 May 2023 00:10:45 +0800 Subject: [PATCH 1125/1723] 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 e8ec1286f4d012428b6419b20ff9faa108fdbd48 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 21 May 2023 04:31:12 +0800 Subject: [PATCH 1126/1723] 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 28958289d812e82795013fe3c15e73985fa82acb Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 21 May 2023 04:39:28 +0800 Subject: [PATCH 1127/1723] 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 d2b57f7b8222455df97db78466b402e94b10d0b4 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 21 May 2023 04:46:06 +0800 Subject: [PATCH 1128/1723] 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 c5ea9b0e488ffe2fb93e5bc244283bc00568f9ab Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 21 May 2023 05:30:29 +0800 Subject: [PATCH 1129/1723] 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 8d201b4afcc886c0cd13903d036d462a1e3d6135 Mon Sep 17 00:00:00 2001 From: liuzitan Date: Sun, 21 May 2023 07:39:27 +0800 Subject: [PATCH 1130/1723] virtio-gpu: Adjust import order of module Signed-off-by: liuzitan --- 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 2669d3117ccbe2c99218f4ab835a90e906ce7314 Mon Sep 17 00:00:00 2001 From: liuzitan Date: Mon, 5 Jun 2023 03:53:30 +0800 Subject: [PATCH 1131/1723] virtio-gpu: Refactor logic of data transfer 2D Signed-off-by: liuzitan --- 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 fe3347bc7606a87a1a2d4245265079de938e6ece Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 11 Jun 2023 17:48:43 +0800 Subject: [PATCH 1132/1723] 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 dc9de2e54..f64f92323 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -640,12 +640,12 @@ pub fn iov_to_buf_direct(iovec: &[Iovec], offset: u64, 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 37697cdf3ac698625272cddac685c1e3c8a6e6b0 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 12 Jun 2023 05:27:13 +0800 Subject: [PATCH 1133/1723] 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 f9d5b4655ffef184ca3776b872d7283c7646951a Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 10 Jun 2023 17:42:57 +0800 Subject: [PATCH 1134/1723] 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 5306bbe2b7f2d9c87f79deb774fcd82e02bbf3b7 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 16 Jun 2023 12:20:21 +0800 Subject: [PATCH 1135/1723] 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 a86e39b9314f8e3335ba47ba70b8d8edb9b51115 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 19 Jun 2023 16:44:22 +0800 Subject: [PATCH 1136/1723] 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 307041a6d691678adb2f17e3d6556b18ccaa5d36 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 19 Jun 2023 22:20:56 +0800 Subject: [PATCH 1137/1723] 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 d011f3e54f53f83ff89e806836b8237099257060 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 20 Jun 2023 11:05:37 +0800 Subject: [PATCH 1138/1723] 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 13519edb8c17b6aeca8f357c94e762ae82c5f1d3 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 18 May 2023 06:12:41 +0800 Subject: [PATCH 1139/1723] 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 e58254c214c7007261303337e09b7d305bd25589 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 18 May 2023 04:39:20 +0800 Subject: [PATCH 1140/1723] 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 1477a8ba410356b1608f5e4ecaaeff2bddd6c17f Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 18 May 2023 05:49:32 +0800 Subject: [PATCH 1141/1723] 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 c4c8f5bea08756d047653151410662097c34b7d2 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 19 Jun 2023 20:37:58 +0800 Subject: [PATCH 1142/1723] 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 8a72018fc9edb00bcfc65f7d806c4708e8b4b4e5 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 18 May 2023 07:10:59 +0800 Subject: [PATCH 1143/1723] 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 ed83dd2f1..19131e5d6 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -948,11 +948,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 47a9d48947184bf6b09ac8d1196f18a4ffd917ad Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 21 Jun 2023 12:04:52 +0800 Subject: [PATCH 1144/1723] 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 5f8ec9cac0aadb3e9539eb50c472376f806f5c59 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Tue, 13 Jun 2023 23:06:48 +0800 Subject: [PATCH 1145/1723] 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 53f96c5c7..3a4b987dc 100644 --- a/ui/src/gtk/menu.rs +++ b/ui/src/gtk/menu.rs @@ -20,7 +20,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 19131e5d6..2125d0bef 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -956,24 +956,25 @@ pub(crate) fn update_window_size(gs: &Rc>) -> Result<( 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, - 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 141f210cad44cfeb84731f47840ca36d9cd0314e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 23 Jun 2023 14:45:30 +0800 Subject: [PATCH 1146/1723] 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 3d7934385e55f4aa142dcc6257fbc496bbe3fe74 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 23 Jun 2023 15:05:55 +0800 Subject: [PATCH 1147/1723] 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 2125d0bef..39b89d16b 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 4ceb52a3d4d323e6bb3fe7868f9114c5b3d4db18 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sun, 25 Jun 2023 10:45:34 +0800 Subject: [PATCH 1148/1723] 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 b19676c8471a7dc5e85ad0fa10c4efc5bc4cc9eb Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 19 Jun 2023 03:09:28 +0800 Subject: [PATCH 1149/1723] 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 d5946fa2bcffb572d440d9a9336fcd93e77f78d0 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 22 Jun 2023 15:58:24 +0800 Subject: [PATCH 1150/1723] 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 92e07daea58d462d6df2f99c28d3ac858b1191bd Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 25 Jun 2023 14:15:01 +0800 Subject: [PATCH 1151/1723] 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 441ba3eabc3ebf32cabb34c6fcc5c65f7823ba14 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 28 Jun 2023 22:25:17 +0800 Subject: [PATCH 1152/1723] 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 884f8e98a220e3a4e88693d0994d36e3b2dfe4a1 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 28 Jun 2023 20:31:14 +0800 Subject: [PATCH 1153/1723] 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 ab540edff8b8a3cb79f3fd12239adc3d6ce73c07 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 17 Jun 2023 10:25:02 +0800 Subject: [PATCH 1154/1723] 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 abd7171c8c2bb4e5a49dd2d4910b4e6b3bc210e8 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 17 Jun 2023 10:38:38 +0800 Subject: [PATCH 1155/1723] 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 a6def1d1f10fd16dc1a9ebe1c9a114485c1d7cc1 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 16 May 2023 05:13:37 +0800 Subject: [PATCH 1156/1723] 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 4ca95346dd63d278595af581c0ddc99b7bbe4ab4 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 29 Jun 2023 02:05:46 +0800 Subject: [PATCH 1157/1723] 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 c417c2ea0cb7daed3f54be6e37932774b23d2e27 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 29 Jun 2023 19:33:26 +0800 Subject: [PATCH 1158/1723] 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 7c61fba208420a8384d3e2988b272a673e15e6df Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 29 Jun 2023 21:44:15 +0800 Subject: [PATCH 1159/1723] 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 0b2f424026be09ff5832b5cd136cacd05297f4a5 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 3 Jul 2023 15:35:20 +0800 Subject: [PATCH 1160/1723] 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 46a33777ca72ce2d9deb06782de8cf5266f1b3df Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 3 Jul 2023 22:10:37 +0800 Subject: [PATCH 1161/1723] 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 c9f6aaba33875933f1d24853ffbe8c602d83ea57 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 10 May 2023 20:06:04 +0800 Subject: [PATCH 1162/1723] 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 59a952aff..ea971839d 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. @@ -2752,10 +2752,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 a205560aea0655dbc0944f1124650b330abce16c Mon Sep 17 00:00:00 2001 From: Andrey Kazmin Date: Tue, 13 Jun 2023 15:42:34 +0300 Subject: [PATCH 1163/1723] 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 132e1324691c2d59282efbc33449b4a4bd020f91 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 4 Jul 2023 17:07:27 +0800 Subject: [PATCH 1164/1723] 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 09b65682a8ec4af743bca117275a605f9bf7e5c2 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 17 Jun 2023 11:08:02 +0800 Subject: [PATCH 1165/1723] 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 663f24358953eebd72e799ed10cc405cebdc0f9d Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 21 Jun 2023 17:20:35 +0800 Subject: [PATCH 1166/1723] 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 bacfb2290dd416d5216181bfacd1b89089015611 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 21 Jun 2023 16:21:19 +0800 Subject: [PATCH 1167/1723] 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 5b4a5a3ffd9489fcc8bdc41d873c98ed7148c9c8 Mon Sep 17 00:00:00 2001 From: Xiao Ye Date: Wed, 21 Jun 2023 16:39:05 +0800 Subject: [PATCH 1168/1723] 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 7c0c2e36df248ced614bd4829fd5cac4913caa97 Mon Sep 17 00:00:00 2001 From: Xiao Ye Date: Wed, 28 Jun 2023 14:08:04 +0800 Subject: [PATCH 1169/1723] 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 296ca6eddb25fdf3728580b9dca3856acd5e0be9 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 24 Jun 2023 12:08:19 +0800 Subject: [PATCH 1170/1723] 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 f4568b85a50198880f265e5be43ffb041c30fcb7 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 24 Jun 2023 14:45:24 +0800 Subject: [PATCH 1171/1723] 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 079c6ac180db0125495b42f89d6b1f57b318bf19 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 3 Jul 2023 14:54:11 +0800 Subject: [PATCH 1172/1723] 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 0a84b5cd8bde62ff69ec2b5afafc8e588a1fc9ce Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 5 Jul 2023 10:34:33 +0800 Subject: [PATCH 1173/1723] 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 936faee6239d7e204d9df9d157e6bf8f2a0e5a80 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 29 Jun 2023 09:30:19 +0800 Subject: [PATCH 1174/1723] 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 a15c586e8e92ffd1af3f4a591316f0f322b39aa0 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 4 Jul 2023 21:50:19 +0800 Subject: [PATCH 1175/1723] 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 a8377c894443858265445558ffd7fef1bf0dc25a Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 8 Jul 2023 16:21:58 +0800 Subject: [PATCH 1176/1723] 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 9d803a979f36acf3c3d866118722f4987267e9be Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 26 Jun 2023 08:37:02 +0800 Subject: [PATCH 1177/1723] 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 681175f34eb09d7871533ed007ba5277be0871cd Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 26 Jun 2023 11:45:32 +0800 Subject: [PATCH 1178/1723] 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 3579fb53ff97964c7a645da19b1aad5cf940971d Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 7 Jul 2023 13:18:37 +0800 Subject: [PATCH 1179/1723] 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 3d1fbd8aca025e8663a19b358fc5f2726eca34c6 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 7 Jul 2023 13:37:53 +0800 Subject: [PATCH 1180/1723] 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 5229f9a2867778664e95665ad4ed2b85d1cbc455 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 8 Jul 2023 03:28:09 +0800 Subject: [PATCH 1181/1723] 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 f3cbcaba8bd84dafc1e58045f5950e15a0a49054 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 3 Jul 2023 22:26:07 +0800 Subject: [PATCH 1182/1723] 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 97c359237910492c5d5d25b6b731a796682dadef Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 4 Jul 2023 02:27:49 +0800 Subject: [PATCH 1183/1723] 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 3c93ebb11caac0c6b0ac0b9bec1bcccfb923797e Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 2 Jul 2023 23:33:53 +0800 Subject: [PATCH 1184/1723] 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 beb960f2e324c8a8e3356388d01ca52890d0c75b Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 8 Jul 2023 10:51:01 +0800 Subject: [PATCH 1185/1723] 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 ea9be0502fbdf3f35d4e021a9bf4b371baa94fa0 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 10 Jul 2023 16:37:22 +0800 Subject: [PATCH 1186/1723] 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 b1333ea6871511ef359e99f1a78bd7108355a9a8 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 4 Jul 2023 14:38:24 +0800 Subject: [PATCH 1187/1723] 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 c1a9bf40d7b3695438776d17f6ce8b38cd7430d3 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Thu, 4 May 2023 10:23:59 +0800 Subject: [PATCH 1188/1723] 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 37dd9549206f0b31c989711efd75c8ca18e3c94c Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 4 Jul 2023 15:52:27 +0800 Subject: [PATCH 1189/1723] 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 f6b81a99776456a9ebb6a8740964dd465cd73e16 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Thu, 4 May 2023 12:15:33 +0800 Subject: [PATCH 1190/1723] 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 28ca63c3e208c666181d3056a32b7d4c07541eec Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Mon, 10 Jul 2023 11:08:57 +0800 Subject: [PATCH 1191/1723] 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 55cbe61dad8a2479b3eb4bfb0a049592ac1176df Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Thu, 4 May 2023 15:12:36 +0800 Subject: [PATCH 1192/1723] 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 6fdcdbc6e6565e703cadf5f472fb18b9dda9c092 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Wed, 28 Jun 2023 17:15:42 +0800 Subject: [PATCH 1193/1723] 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 a5bfad58e71efb4d8392caec552200f32be7bf2a Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Thu, 4 May 2023 15:56:43 +0800 Subject: [PATCH 1194/1723] 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 ecd9f68618169d3ea0be387ac2aa04d04fccb219 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 11 Jul 2023 01:48:00 +0800 Subject: [PATCH 1195/1723] 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 7a0432486d961e33e5bf3b0df0df1ad2fc5ee88f Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 4 Jul 2023 18:15:23 +0800 Subject: [PATCH 1196/1723] 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 b309f84d00ed207ada8eb06f6744a33e1ed33ce0 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 28 Jun 2023 09:23:39 +0800 Subject: [PATCH 1197/1723] 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 048aef0c95a2a2fbcfe57b45088b85e1c39f988a Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 4 Jul 2023 17:08:43 +0800 Subject: [PATCH 1198/1723] 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 f1087609a197370ece8aabdc1bd2d962aac4db2b Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sat, 1 Jul 2023 10:08:05 +0800 Subject: [PATCH 1199/1723] 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 239aa9dcf97aa79e4a465449e35250e7aa3bd89d Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 8 May 2023 16:46:14 +0800 Subject: [PATCH 1200/1723] 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 23ec70e6c43ba3849fafb99bb2a8408df4df17f0 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 9 May 2023 13:22:24 +0800 Subject: [PATCH 1201/1723] 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 f0119b2cb4716a81f89c4718ba42b82c1bdc8164 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 13 Jul 2023 10:37:54 +0800 Subject: [PATCH 1202/1723] 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 ba0739c6d4b75fcc58d623713cf8d8fc0b640f78 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Tue, 6 Jun 2023 19:23:44 +0800 Subject: [PATCH 1203/1723] 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 d9d843496a4c1a698b7ca027364e2a28dd1884c0 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 13 Jul 2023 23:46:56 +0800 Subject: [PATCH 1204/1723] 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 de3bf7f18b16285161ee78f94a3b1fe0c2acdd73 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 14 Jul 2023 00:34:04 +0800 Subject: [PATCH 1205/1723] 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 158085e3c798f57d44693939ddf320c2ff169f8d Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 4 Jul 2023 18:06:24 +0800 Subject: [PATCH 1206/1723] 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 97d56af0c..c107d53a3 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 { @@ -738,6 +739,7 @@ impl Qcow2Driver { self.free_cluster( new_snapshot_table_offset, new_snapshots_table_clusters, + true, &Qcow2DiscardType::Never, )?; } @@ -902,6 +904,7 @@ impl Qcow2Driver { self.free_cluster( new_snapshot_table_offset, num_clusters, + true, &Qcow2DiscardType::Snapshot, )?; } @@ -912,6 +915,7 @@ impl Qcow2Driver { self.free_cluster( new_l1_table_offset, l1_table_clusters, + true, &Qcow2DiscardType::Snapshot, )?; } @@ -1254,6 +1258,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 b7a9c871f..de7e71654 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 921d6ac45bde74713b797dbfc74701abfdc9474d Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 11 Jul 2023 15:18:55 +0800 Subject: [PATCH 1207/1723] 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 c107d53a3..7b2defa07 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")?; @@ -1675,6 +1675,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, @@ -1682,6 +1683,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()) @@ -1949,6 +1952,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, @@ -1956,6 +1960,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); @@ -1979,6 +1985,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, @@ -1986,6 +1993,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 de7e71654..4d414905f 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 { @@ -672,6 +679,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, @@ -679,6 +687,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 a78246dbd..615b02ac5 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 @@ -1388,78 +1390,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, @@ -2009,6 +1950,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 539b31925..a1bb53dd7 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 86883cf28..230737e88 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -764,6 +764,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 d262e91ff63d13380fd2fbaacc69b9d601951945 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 18 Jul 2023 16:05:25 +0800 Subject: [PATCH 1208/1723] 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 c61b3f1955af1476c907b5be79aba76360b44339 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 13 Jul 2023 16:48:28 +0800 Subject: [PATCH 1209/1723] 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 74f8b99e98ad45637fcf4f765bf369eaf42a33d2 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 15 Jul 2023 16:00:14 +0800 Subject: [PATCH 1210/1723] 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 8e8edd01c8ae2f7d8b4d10f4cd123fd6faa8fe2a Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 15 Jul 2023 05:59:26 +0800 Subject: [PATCH 1211/1723] 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 1f6c7db71..5562b3c97 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -92,7 +92,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, @@ -448,7 +449,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() @@ -492,6 +492,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 4789b85805f5ec9f5847b9de745033e563cbdadd Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 17 Jul 2023 10:23:30 +0800 Subject: [PATCH 1212/1723] 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 d2f0906613379a94b2e7a0d0ef29fa4aadf0769f Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 16 Jul 2023 16:36:12 +0800 Subject: [PATCH 1213/1723] 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 74e985aae..3aa47153f 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 e786a36fba051dc910b15bc2c1696b8879d56c89 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 15 Jul 2023 10:51:33 +0800 Subject: [PATCH 1214/1723] 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 d7fdfdb48b95b9f0c3106e224f669e1aa7df410f Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 19 Jul 2023 19:56:34 +0800 Subject: [PATCH 1215/1723] 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 120d11c9a57fbd4e8e52037372f2add4ab68733a Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 15 Jul 2023 16:44:08 +0800 Subject: [PATCH 1216/1723] 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 a8acd7cd1906c600512a830bc8839d158661577e Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 15 Jul 2023 17:53:28 +0800 Subject: [PATCH 1217/1723] 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 7d0c28840b45a952b7392b6575037e4719453eab Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 16 Jul 2023 01:55:59 +0800 Subject: [PATCH 1218/1723] 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 46da816a640e2b84c178cb5dbbf73cb7c1c40a68 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 22 Jul 2023 17:39:52 +0800 Subject: [PATCH 1219/1723] 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 1a0ffd9254c314634abbc9f31794cb09049fe296 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 21 Jul 2023 21:49:02 +0800 Subject: [PATCH 1220/1723] 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 2fb4e367f13ef3eadf11336ae87dd822defd9087 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 22 Jul 2023 03:29:59 +0800 Subject: [PATCH 1221/1723] 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 96a9cb3bce1501b0a85b3f1f1797eb5ba216b8fc Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 10 Jul 2023 13:10:46 +0800 Subject: [PATCH 1222/1723] 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 8460ba3c4a322e41a8a0f850e485b0fd222457eb Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 15 Jul 2023 13:40:50 +0800 Subject: [PATCH 1223/1723] 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 e474b68a1c467fa2429b7e5d74c649ea55d70aec Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 20 May 2023 17:00:05 +0800 Subject: [PATCH 1224/1723] 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 8dd40361a..b5d732b7a 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 @@ -160,6 +160,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 b1f340bf9..842413bcb 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, @@ -248,6 +248,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 6a04f2653..488298aa2 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, @@ -250,6 +250,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 113cef937..5f0e35d4b 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, ); @@ -1553,6 +1567,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 de06c3890..5ec5c4b91 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -284,6 +284,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 a70991c00..2a79f1d0e 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 2f83d001c5133ef929418ebc40a6c01f0988be20 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 20 May 2023 17:03:57 +0800 Subject: [PATCH 1225/1723] 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 f371b4e34d02927feeb5266f6972def20c861555 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 20 May 2023 17:08:04 +0800 Subject: [PATCH 1226/1723] 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 5f0e35d4b..545a7e32c 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; } @@ -779,10 +780,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) @@ -808,7 +812,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 85676e4993d61b1eef94e774346782abe6e478f4 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 26 Jul 2023 10:17:29 +0800 Subject: [PATCH 1227/1723] 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 73194073de452cb88d26b01ff5d3f2fe05d83816 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 26 Jul 2023 10:18:07 +0800 Subject: [PATCH 1228/1723] 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 fe114f7faa4510826b9fa7badb23a4410d4e8311 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 25 Jul 2023 19:48:46 +0800 Subject: [PATCH 1229/1723] 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 04263f7f99ea17023c8c2bb9dba164aeb2cf6e55 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Mon, 24 Jul 2023 16:58:16 +0800 Subject: [PATCH 1230/1723] =?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 ba021c14e8dc0611eeaa2332061bbe108cb08a17 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Fri, 28 Jul 2023 14:25:12 +0800 Subject: [PATCH 1231/1723] 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 6f1994b436ed2f1da0dd5becea434edbb4775cd5 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 27 Jul 2023 20:17:04 +0800 Subject: [PATCH 1232/1723] 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 b8e7552390047052b0ef8fffeb8f521c8946bf56 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 28 Jul 2023 18:06:23 +0800 Subject: [PATCH 1233/1723] 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 dba988bd631b6d94eb6850113f8e0462562442b8 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 27 Jul 2023 17:07:34 +0800 Subject: [PATCH 1234/1723] 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 74dee7c6e77d70f90c8f89cdff427f805bb1c70f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 9 Jul 2023 19:45:21 +0800 Subject: [PATCH 1235/1723] 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 d97b2a68b4c71180519c7b6d0fcc98e79c60b813 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 9 Jul 2023 17:41:15 +0800 Subject: [PATCH 1236/1723] 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 a104cd4abaca8796cd73e49c44ac07e019239630 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 22 Jul 2023 10:19:22 +0800 Subject: [PATCH 1237/1723] 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 30b0ca11ada51d524bf401f785a8b0d7cad053b3 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 9 Jul 2023 20:05:25 +0800 Subject: [PATCH 1238/1723] 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 6894015b3492e5282ad1ef27e6485fac25f72fa9 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 21 Jul 2023 17:46:18 +0800 Subject: [PATCH 1239/1723] 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 dff54c1078344d7fac21f26d1bdd3ab144395cae Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 9 Jul 2023 22:06:41 +0800 Subject: [PATCH 1240/1723] 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 352ab4c3994254e46ad226401fd4dbbf72b9172f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 22 Jul 2023 20:40:28 +0800 Subject: [PATCH 1241/1723] 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 a627ae684f4ffb07c30c190ad0dcbe54b12529ed Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 21 Jul 2023 18:13:15 +0800 Subject: [PATCH 1242/1723] 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 6f5c8914d7669b5326bf937c9251ff0df292ccb6 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 22 Jul 2023 11:13:10 +0800 Subject: [PATCH 1243/1723] 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 b551fbf8e6902e7028f3f8c2c862355637370154 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 22 Jul 2023 11:13:34 +0800 Subject: [PATCH 1244/1723] 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 b57558e8bc4824b0a6333859f7b66ddf3bc2f8d7 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 31 Jul 2023 16:48:43 +0800 Subject: [PATCH 1245/1723] 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 6d9e66f6750a9ee887b33d056d8e9bc1023411da Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 31 Jul 2023 16:48:27 +0800 Subject: [PATCH 1246/1723] 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 b80c250fa8131e1928e41dfadff1acfcbc1a95e0 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 29 Jul 2023 11:27:58 +0800 Subject: [PATCH 1247/1723] 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 f916215d84bd8d909fcf5604799f5d15178280fb Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 1 Aug 2023 16:37:06 +0800 Subject: [PATCH 1248/1723] 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 231eee7036045ce9445e63955615a212911705eb Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 2 Aug 2023 11:08:11 +0800 Subject: [PATCH 1249/1723] 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 f9c716dc6bd091f8fceca87a4367cecd724d5d9e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 2 Aug 2023 15:43:05 +0800 Subject: [PATCH 1250/1723] 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 96d5cb72fa938f445878e971878eb1d022170030 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 1 Aug 2023 16:37:06 +0800 Subject: [PATCH 1251/1723] 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 b8f504ed121ca708cfc39eefd431674f21d8500d Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Wed, 2 Aug 2023 11:55:50 +0800 Subject: [PATCH 1252/1723] 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 30ba5d29a3500938ff4294147797c5ec814c90d8 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 1 Aug 2023 19:34:47 +0800 Subject: [PATCH 1253/1723] 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 15797a2a2511571ca0b1b1be62c7bcb0c69ebc8b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 1 Aug 2023 19:51:08 +0800 Subject: [PATCH 1254/1723] 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 65c055e071154271450c931130095817de5d875b Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Thu, 3 Aug 2023 10:25:26 +0800 Subject: [PATCH 1255/1723] 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 15ae33712d221305b398e6d10576ae2c80526ec3 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 4 Aug 2023 09:50:52 +0800 Subject: [PATCH 1256/1723] 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 9951ce30c4b45e8d62fa6e2767af92a75b5660e8 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 4 Aug 2023 09:46:58 +0800 Subject: [PATCH 1257/1723] 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 2c6d4f834c5e39c37f7807203e3e2385c20d5f79 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 5 Jul 2023 04:55:37 +0800 Subject: [PATCH 1258/1723] 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 d32d4f4216bf380b7a74076c89d26c6dfa7c4746 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 20 Jul 2023 08:46:05 +0800 Subject: [PATCH 1259/1723] 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 ad60d00457816ab4de496c57779f79482cc647f1 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 4 Jul 2023 16:44:44 +0800 Subject: [PATCH 1260/1723] 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 c104f293979638f36b9b35186b2b7fd1236da357 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Fri, 4 Aug 2023 15:12:10 +0800 Subject: [PATCH 1261/1723] 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 e050189b94c08b6df888fb0932fcca8a3a3e0c3d Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 10 Jul 2023 21:33:32 +0800 Subject: [PATCH 1262/1723] 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 97b1d1e681aac85f92117546a79b72266c4425f1 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 7 Aug 2023 14:29:02 +0800 Subject: [PATCH 1263/1723] 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 63b74b24d89e23d28e9c8ef55c9581c4a3398a48 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 7 Aug 2023 11:40:44 +0800 Subject: [PATCH 1264/1723] 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 1f3160d4e290a22cfd47496c8c6d52cc5d9bcb40 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 14 Jul 2023 15:48:24 +0800 Subject: [PATCH 1265/1723] 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 97c6e6851373b07ebe8af3ddb5274cd5f7b98062 Mon Sep 17 00:00:00 2001 From: wangmeiling Date: Tue, 8 Aug 2023 06:44:25 +0800 Subject: [PATCH 1266/1723] 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 729afc57aed64834e68894ce53cebfd219c2968a Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 7 Aug 2023 10:34:52 +0800 Subject: [PATCH 1267/1723] 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 b9015baf89bacbf0fc5a8d27f151211f354a6280 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 8 Aug 2023 14:54:34 +0800 Subject: [PATCH 1268/1723] 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 e8e8b5a1b8a02337320a97b2a392021d5393db2b Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 8 Aug 2023 19:37:55 +0800 Subject: [PATCH 1269/1723] 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 0bb9a63345036400caa37d9dc0901d5d7d399143 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 16 Jul 2023 12:38:16 +0800 Subject: [PATCH 1270/1723] 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 3861e581b1bfd32bb22cbb19da9a7c8a7f0861af Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 16 Jul 2023 14:17:41 +0800 Subject: [PATCH 1271/1723] 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 fdd166140226741fb66ffdc675739142e950a472 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 16 Jul 2023 23:02:38 +0800 Subject: [PATCH 1272/1723] 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 8888fe93f0d37582ce0087b9b4150f18a736df9c Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 17 Jul 2023 04:59:40 +0800 Subject: [PATCH 1273/1723] 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 7a7f1435e9c7eeb345c46bc517554037e17b59f4 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 1 Aug 2023 17:10:04 +0800 Subject: [PATCH 1274/1723] 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 67793115ca68f4e4e0229dc99485d9aeecbc60ce Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 31 Jul 2023 17:51:11 +0800 Subject: [PATCH 1275/1723] 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 452c466a1953046af64eb71810dd8cbd76c24214 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 7 Aug 2023 10:52:26 +0800 Subject: [PATCH 1276/1723] 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 ad7fbae1b6303c5361c5a8d71df41156542851cb Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 7 Aug 2023 15:23:26 +0800 Subject: [PATCH 1277/1723] 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 39741afa8b1996691f348c18e29f0ecbac823d41 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 7 Aug 2023 19:22:45 +0800 Subject: [PATCH 1278/1723] 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 f9f9f84f024ef1e8f66b049dc10d4016a3797455 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 9 Aug 2023 09:38:46 +0800 Subject: [PATCH 1279/1723] 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 74c9827d52c7397dcca7cf4e61dbd537d7f01cfa Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 8 Aug 2023 03:50:42 +0800 Subject: [PATCH 1280/1723] 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 98d38779a5cbf8d1e5ec7bc180d744a0bcd10eaf Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 23 Jul 2023 06:37:23 +0800 Subject: [PATCH 1281/1723] 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 ea0b2d0ac..972150a43 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -2226,13 +2226,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 ece5dbab78b91dc739880d56cbe5864b583f7aee Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Tue, 8 Aug 2023 21:53:31 +0800 Subject: [PATCH 1282/1723] 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 a8f1b60a50bd867d07b88ddc1786e5fc5d2f98b8 Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Tue, 8 Aug 2023 22:23:23 +0800 Subject: [PATCH 1283/1723] 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 b00fddd3837e8816dc3050eabd31416ad32581fd Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Tue, 8 Aug 2023 23:01:00 +0800 Subject: [PATCH 1284/1723] 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 7e9d5c1479ee421efd29e21a59ba81b82b87f2ac Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 9 Aug 2023 09:56:10 +0800 Subject: [PATCH 1285/1723] 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: Fei Xu 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 43222207ee2036916975f3c56d2d6cf79cb07192 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 9 Aug 2023 09:57:54 +0800 Subject: [PATCH 1286/1723] 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 57df13b64ea3d429f8827e38fa20b8807a133073 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 9 Aug 2023 10:10:21 +0800 Subject: [PATCH 1287/1723] qcow2: optimize the qcow2 performance Do not write metadata directly after modified. Use timer to minus the sync write for metadata. Signed-off-by: Fei Xu 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 86457f85322618c473e47fb374440dc241fce19e Mon Sep 17 00:00:00 2001 From: mayunlong Date: Wed, 9 Aug 2023 22:56:04 +0800 Subject: [PATCH 1288/1723] 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 41c457f40f274c2db8ba4735f7e112a11542157c Mon Sep 17 00:00:00 2001 From: mayunlong Date: Thu, 10 Aug 2023 21:03:42 +0800 Subject: [PATCH 1289/1723] 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 3a3aba9b497c7d070712895eea613daa092569bf Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 9 Aug 2023 14:49:44 +0800 Subject: [PATCH 1290/1723] 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 d2659c184ad5291d99802ff1a2b567dcf39e8055 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 16 Jul 2023 04:53:21 +0800 Subject: [PATCH 1291/1723] 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 ae500e130d6cef3e1777df0216679c5e2a69feeb Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 11 Aug 2023 15:07:08 +0800 Subject: [PATCH 1292/1723] 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 87d45c13c2c5b099929b962ec2442fcccd36451a Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 15 Aug 2023 14:12:30 +0800 Subject: [PATCH 1293/1723] 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 96b25ec3db3d836025d7f876325c5185ffbd0683 Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Fri, 11 Aug 2023 19:27:18 +0800 Subject: [PATCH 1294/1723] 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: boby.chen --- .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 982918eb8..0da6af880 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 4ae701c29ae0df8ca8756f1e6c051f0a5e7ec374 Mon Sep 17 00:00:00 2001 From: wangmeiling Date: Wed, 16 Aug 2023 10:23:55 +0800 Subject: [PATCH 1295/1723] 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 893cc95ed8ad41c3e37c077e1f89cdbed810640c Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 14 Aug 2023 14:38:02 +0800 Subject: [PATCH 1296/1723] 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 25ed4cebd..81467ffc6 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 f5f22f0205d41a677ea0a277d9d30d13eb0fad9a Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 14 Aug 2023 16:38:59 +0800 Subject: [PATCH 1297/1723] 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 4a1a5bc12..86fa34717 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 12957a15fdd4258713f4e4f432e2f9dbea7ee221 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 17 Aug 2023 20:37:14 +0800 Subject: [PATCH 1298/1723] 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 d5ca9f0d637af8d384e0ae4514227d50893139da Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 18 Aug 2023 17:52:56 +0800 Subject: [PATCH 1299/1723] 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 4cd5feec77418653a760d33e600aa80f20bdd9df Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 18 Aug 2023 17:18:26 +0800 Subject: [PATCH 1300/1723] 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 0c0589d178af8b989c69fbc0fb19d9349c8db4d0 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 17 Aug 2023 20:41:17 +0800 Subject: [PATCH 1301/1723] 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 764c5e641f8e99d73e8e89ac205245e29853d78c Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 14 Aug 2023 18:28:45 +0800 Subject: [PATCH 1302/1723] 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 76063e34827dbb341dc58bd59ae823a081bea686 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 7 Aug 2023 19:04:44 +0800 Subject: [PATCH 1303/1723] 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 b0f99fbdf..2eeba3545 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 0da6af880..de74eccce 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 fdfbe6373be5efb4118aaa6e7ae7c11d68ff36ba Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 17 Aug 2023 16:53:17 +0800 Subject: [PATCH 1304/1723] 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 2c3fa3ad605f4dd6621618610b91d06400f75a86 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sat, 19 Aug 2023 16:51:57 +0800 Subject: [PATCH 1305/1723] 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 354744897c61465430b2d2e304a5852ebaa69f35 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 17 Aug 2023 16:34:21 +0800 Subject: [PATCH 1306/1723] 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 c4b702b2009260a08d3a3cd2417188749054c06c Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 7 Aug 2023 19:59:08 +0800 Subject: [PATCH 1307/1723] 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 a9e08accddb75a47c313213b5a18e806c4f76ff6 Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Tue, 15 Aug 2023 11:11:22 +0800 Subject: [PATCH 1308/1723] build: Add feature optional usb_camera Add new "usb_camera" build feature which enables usb-camera by `--features usb_camera`. Signed-off-by: boby.chen --- 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 c04ce55c5e1f71a3ab2c99942143641226e6f9db Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 22 Aug 2023 11:04:00 +0800 Subject: [PATCH 1309/1723] 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 c8d8d32d823431d9aa29f4b60d18226d4c359aab Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 22 Aug 2023 20:55:03 +0800 Subject: [PATCH 1310/1723] 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 52a42cd871f604218385d673313362ae5f4cbd8e Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 23 Aug 2023 09:52:58 +0800 Subject: [PATCH 1311/1723] 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 d01e970602bce24393348da943272df457aa3381 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 23 Aug 2023 09:48:25 +0800 Subject: [PATCH 1312/1723] 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 2e54efd634b367992391c347aaaa74f02e75986d Mon Sep 17 00:00:00 2001 From: Liu Wenyuan Date: Mon, 21 Aug 2023 23:27:05 +0800 Subject: [PATCH 1313/1723] 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 f4ec3a547..94afd6904 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1956,8 +1956,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 2df131f95eb850bf5aceb3068ff4c36fa772b9cc Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Thu, 24 Aug 2023 09:32:02 +0800 Subject: [PATCH 1314/1723] 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 da8dd64bb6ee4a9ea174c67ccb5147a511ed0b93 Mon Sep 17 00:00:00 2001 From: Liu Wenyuan Date: Thu, 24 Aug 2023 10:24:34 +0800 Subject: [PATCH 1315/1723] 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 c894f8380e60ca413444478cbe786f87f599b5c3 Mon Sep 17 00:00:00 2001 From: Liu Wenyuan Date: Fri, 25 Aug 2023 13:34:47 +0800 Subject: [PATCH 1316/1723] 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 2543d1ebc4a1edcd6052a5a395ec84b993f8a783 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 23 Jul 2023 04:27:29 +0800 Subject: [PATCH 1317/1723] 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 9803c19afce2e0beb33d9631ab54eb9ddca4b976 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 23 Jul 2023 05:02:12 +0800 Subject: [PATCH 1318/1723] 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 c03a7ee170a4e45ca595e712876e0229f8b3f345 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 26 Jul 2023 09:42:52 +0800 Subject: [PATCH 1319/1723] 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 28500e79994b107dd7dcc35ab3e3ad6470f56fff Mon Sep 17 00:00:00 2001 From: Liu Wenyuan Date: Thu, 24 Aug 2023 16:51:47 +0800 Subject: [PATCH 1320/1723] 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 034a220eb6f0a16842ffbea0a287e7dede98c057 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sun, 2 Jul 2023 18:49:30 +0800 Subject: [PATCH 1321/1723] 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 37d5512173d0f872ecbf94b9af0be5407532ecad Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 7 Aug 2023 16:07:09 +0800 Subject: [PATCH 1322/1723] 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 a1f4bc60e1fa4eef2f83d580856db7b62585ab22 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 16 Aug 2023 19:31:20 +0800 Subject: [PATCH 1323/1723] 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 4abfd75d6d331338ef8410e6b2b00347d368a6e7 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 17 Aug 2023 15:38:56 +0800 Subject: [PATCH 1324/1723] 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 ccb9366198486cd1eb3ae3a5d1beafe758001b65 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 18 Aug 2023 20:33:38 +0800 Subject: [PATCH 1325/1723] 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 0f1bf69e39f81668014f371a8559f45e49fa624d Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 25 Aug 2023 10:28:21 +0800 Subject: [PATCH 1326/1723] 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 0cc20124dc0196fe1f71f04e073c0e6a26f6db11 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 28 Aug 2023 15:32:48 +0800 Subject: [PATCH 1327/1723] 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 360ab595805b941b54bc58188c1e6fc7b5ec7723 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 13 Aug 2023 10:12:55 +0800 Subject: [PATCH 1328/1723] 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 d5fff7461..209d06223 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(); @@ -894,7 +894,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() @@ -941,8 +941,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 81356a5359d9bf669e3876cdc5aa778308f08eaa Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 30 Aug 2023 14:36:54 +0800 Subject: [PATCH 1329/1723] 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 dd3557252a2fb4871519072e19c5f68dea547f8b Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 31 Aug 2023 17:33:38 +0800 Subject: [PATCH 1330/1723] 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 88636f7fa8872ed40b4d0377c82429633884971c Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 31 Aug 2023 18:38:04 +0800 Subject: [PATCH 1331/1723] 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 d78cbd8a3d318342abbe22609b434168ba94aba1 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 31 Aug 2023 21:28:12 +0800 Subject: [PATCH 1332/1723] 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 d6e96c0362ea4540fdf98896ef5dc50736861129 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 27 Jul 2023 19:02:05 +0800 Subject: [PATCH 1333/1723] 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 a5c10b677054d5e0fa56119588b1f15e19ae927b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 2 Sep 2023 00:11:52 +0800 Subject: [PATCH 1334/1723] 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 9cbee2a8948e31c534b725788621f81fe7d6df35 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 2 Sep 2023 13:47:56 +0800 Subject: [PATCH 1335/1723] 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 e0ff91702af903d62e5e7bf6d67ff2ff5f5848e6 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 2 Sep 2023 15:01:28 +0800 Subject: [PATCH 1336/1723] 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 7e7576d3ce7c91cf422ceb7368d5862593c3c3c9 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 5 Sep 2023 19:51:00 +0800 Subject: [PATCH 1337/1723] 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 9e914cec776889c944b97b201e8db5cf54b74fa5 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 24 Aug 2023 15:50:48 +0800 Subject: [PATCH 1338/1723] 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 5979974fb352a3540e4f6a0db047a93b3d973945 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 5 Sep 2023 21:29:47 +0800 Subject: [PATCH 1339/1723] 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 27bc9690ad3fa893d66b040e793f9837457b2cbe Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 4 Sep 2023 16:26:22 +0800 Subject: [PATCH 1340/1723] 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 aee56a961bd425edb97de99272066ede8080b330 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 6 Sep 2023 15:31:31 +0800 Subject: [PATCH 1341/1723] 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 3256cb5be6706c259b084951e6353144e3453875 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 3 Sep 2023 01:34:56 +0800 Subject: [PATCH 1342/1723] 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 9156089c8a1d4d40f2479e90d59ba63f4e0dfd15 Mon Sep 17 00:00:00 2001 From: Liu Wenyuan Date: Mon, 4 Sep 2023 20:50:08 +0800 Subject: [PATCH 1343/1723] 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 529fe3b4859672784c5f4513b345487df16ab466 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 7 Sep 2023 21:11:05 +0800 Subject: [PATCH 1344/1723] 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 41252f5c3f61b072bbaa8c6be65032dafabd87bf Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 7 Sep 2023 21:34:43 +0800 Subject: [PATCH 1345/1723] 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 2a0ca35151c1bf122330dd53f9756aee2d58ebb3 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Mon, 11 Sep 2023 11:47:10 +0800 Subject: [PATCH 1346/1723] 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 e5f8a98d4c0f1152fc545c11e560c3edbb5fdf4b Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Mon, 11 Sep 2023 17:14:00 +0800 Subject: [PATCH 1347/1723] 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 f435610979b4b7a944b212ff1a9b2583489f6a7e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 3 Sep 2023 07:01:52 +0800 Subject: [PATCH 1348/1723] 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 9746bc05f376d2c147016a85988c338b225ccba9 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 3 Sep 2023 07:29:41 +0800 Subject: [PATCH 1349/1723] 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 a280a5cd04b0910e02b364cf6f555529f3cbedaa Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 3 Sep 2023 07:41:57 +0800 Subject: [PATCH 1350/1723] 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 436641c67d30edddeac208cbe14ef81984de7ba1 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 3 Sep 2023 07:51:16 +0800 Subject: [PATCH 1351/1723] 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 03be0180618c2d6405054c8d3857dac7cde841ab Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 3 Sep 2023 07:55:13 +0800 Subject: [PATCH 1352/1723] 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 512ef34c234114ed3fa9c7a8e8d654a6d9c9dcae Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Sun, 3 Sep 2023 08:20:00 +0800 Subject: [PATCH 1353/1723] pci/demo_dev: Move demo_dev into demo_device Signed-off-by: boby.chen --- 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 804aa6ad752bc78b34e8fb11aca395305065840a Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Sun, 3 Sep 2023 08:33:56 +0800 Subject: [PATCH 1354/1723] camera_backend: Refactor for better naming The backend suffix is more proper. Signed-off-by: Fei Xu --- 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 cdd786b27a775fb0fa76b6f244ece2d5dd8b678f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 3 Sep 2023 08:41:44 +0800 Subject: [PATCH 1355/1723] 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 953ec917f8dff78062380d2d5fb6ad2f9482bbac Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Fri, 8 Sep 2023 16:58:48 +0800 Subject: [PATCH 1356/1723] 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 37295beba7f72eb3fcc8bd0122583b0fd34faa93 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 12 Sep 2023 20:21:16 +0800 Subject: [PATCH 1357/1723] 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 ad2c71765b87ab775ae3c6f674d3ab5c41f8922e Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 12 Sep 2023 20:31:47 +0800 Subject: [PATCH 1358/1723] 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 c7e4f83c70d5989c9f970fbcad67b6527bac9688 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 20 Sep 2023 12:34:50 +0800 Subject: [PATCH 1359/1723] 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 2eb4ed2f8df15ca6290477e950a245c9729858d2 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 20 Sep 2023 12:36:16 +0800 Subject: [PATCH 1360/1723] 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 15a15b3564f2f528bf670bf3fda76171155038d3 Mon Sep 17 00:00:00 2001 From: Liu Wenyuan Date: Tue, 12 Sep 2023 10:34:09 +0800 Subject: [PATCH 1361/1723] 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 55a7e77d0951d33b88f188dbc17e679201c93731 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 26 Oct 2023 19:55:41 +0800 Subject: [PATCH 1362/1723] 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 7a7bb5745a7940022715064a92cf22ac9504ba7c Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 12 Sep 2023 16:48:06 +0800 Subject: [PATCH 1363/1723] 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 a043bfab5a8a3b27ffac9437738d638641bf846c Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 12 Sep 2023 17:07:04 +0800 Subject: [PATCH 1364/1723] 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 caf76cd6ce45525ef20eb7352a6c6684be075968 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Fri, 22 Sep 2023 10:40:47 +0800 Subject: [PATCH 1365/1723] 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 ed420d952aaee917ce83d44c209dd0db5d5a56a7 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 13 Sep 2023 10:25:43 +0800 Subject: [PATCH 1366/1723] 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 2c709a40c7b1ebae7ee89fba782ed52d6a456818 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 13 Sep 2023 11:10:43 +0800 Subject: [PATCH 1367/1723] 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 ab019ade50f163699aca5ea04d8c397ab628c5bb Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Fri, 15 Sep 2023 14:59:55 +0800 Subject: [PATCH 1368/1723] 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 2da6b3955cda0afbf83ebb5ac48d54a053c947c2 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Fri, 22 Sep 2023 11:30:47 +0800 Subject: [PATCH 1369/1723] 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 d52d67729f3fc3a7800296def490145c94fbd306 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 25 Jul 2023 08:32:59 +0800 Subject: [PATCH 1370/1723] 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 e66b70107b8321f321a4b81caa352dde2b413d1b Mon Sep 17 00:00:00 2001 From: prostous Date: Thu, 28 Sep 2023 13:48:29 +0300 Subject: [PATCH 1371/1723] 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 23085c1ff4c084b797a8e96889d4d91eb05d47df Mon Sep 17 00:00:00 2001 From: zhangdianndian0127 <1635468471@qq.com> Date: Sun, 8 Oct 2023 02:15:16 +0800 Subject: [PATCH 1372/1723] 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 20f7e2a3d77e0433352feb2fc104da1180bacd54 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 8 Oct 2023 15:31:27 +0800 Subject: [PATCH 1373/1723] 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 45e67d77a48f924eee44cefb8bb7e20b0e6b2058 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 20:05:55 +0800 Subject: [PATCH 1374/1723] 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 83d87ba305ec04a2f35d7ece1aa4fea6bd47f955 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 14:56:24 +0800 Subject: [PATCH 1375/1723] 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 8640b2d4884a96c239da2e07206c7d4087d610ae Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 19:08:21 +0800 Subject: [PATCH 1376/1723] 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 2c1906fc4e0d8fa2f1841f4ef01145118e6c358c Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 14:59:14 +0800 Subject: [PATCH 1377/1723] 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 ce845043e..7a4ac9ca0 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; @@ -2018,12 +2018,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 766ddaa6c5938153f3996aa7ec0274369f03fd19 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 04:39:43 +0800 Subject: [PATCH 1378/1723] 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 20f272516319145bda06363dd17b3e251d9f9509 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 05:11:27 +0800 Subject: [PATCH 1379/1723] 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 cc3bf240bb97a65b8131898e89cc38461942d8f5 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 05:15:13 +0800 Subject: [PATCH 1380/1723] 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 5fc36499a7f2343365c098ca021adcc01ac98928 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 05:25:43 +0800 Subject: [PATCH 1381/1723] 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 d5e64e125669fa0a5f4d715259e5b2e8b6e6fdf2 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 05:42:23 +0800 Subject: [PATCH 1382/1723] 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 3903edb4ced2bd3b66ad4e8d66760e83c6e04356 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 05:49:42 +0800 Subject: [PATCH 1383/1723] 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 baa89ffbd1ea40c6d09432c199e529b194f416d2 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 05:53:30 +0800 Subject: [PATCH 1384/1723] 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 83bfadc31c8f687d204e1d002f3ebc41bbe7910c Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sun, 8 Oct 2023 09:40:58 +0800 Subject: [PATCH 1385/1723] 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 c4bfb847025782d9897766360ef5f44f7df21dd4 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sun, 8 Oct 2023 09:55:21 +0800 Subject: [PATCH 1386/1723] 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 2083202f5f71f0ff28848ab12fd55f63886bbcef Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 9 Oct 2023 16:57:47 +0800 Subject: [PATCH 1387/1723] 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 36d23994e6657210da05e8aeba150aacf51e86d3 Mon Sep 17 00:00:00 2001 From: ZhiGang Date: Thu, 12 Oct 2023 08:52:52 +0000 Subject: [PATCH 1388/1723] =?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 486b5cd66b83007bdf94c99d0b8889adff433f5b Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 18 Sep 2023 15:27:27 +0800 Subject: [PATCH 1389/1723] 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 17785dbfd6ac06394ae6f6d2ae48e3ab3ba11765 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 18 Sep 2023 15:38:08 +0800 Subject: [PATCH 1390/1723] 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 c7b2bc494fc7d209fc8f678cc7c19eec4864bd46 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 19 Sep 2023 01:15:52 +0800 Subject: [PATCH 1391/1723] 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 96b020db79de070959018c7dbc2a9fe9bec4abc3 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 19 Sep 2023 00:27:57 +0800 Subject: [PATCH 1392/1723] 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 fa30d873888b6165151b2f3406b113d1701ef53a Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 19 Sep 2023 03:17:05 +0800 Subject: [PATCH 1393/1723] 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 08d2f7e121f2e9cad282c4685dc488a9009aec1e Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 9 Nov 2023 17:30:49 +0800 Subject: [PATCH 1394/1723] 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 23fa5331a2f3dd9bffe86a03bcbd35b55b64590a Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 12 Nov 2023 04:23:11 +0800 Subject: [PATCH 1395/1723] 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 bd3c683f11f2097999603896616ddfee9b10bb7a Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 20 Nov 2023 21:20:52 +0800 Subject: [PATCH 1396/1723] 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 5fe1263b4..1c71f9694 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(); @@ -509,11 +518,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 { @@ -531,7 +541,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 9965ba8b33269cea3fdc2011ea79756a66f7e09c Mon Sep 17 00:00:00 2001 From: wangmeiling Date: Wed, 22 Nov 2023 15:30:15 +0800 Subject: [PATCH 1397/1723] 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 2db11aac5..cc71f5aed 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::{ @@ -113,7 +113,7 @@ trait CtrlHdr { } #[repr(C)] -#[derive(Default, Clone, Copy)] +#[derive(Default, Clone, Copy, Debug)] struct VirtioGpuCtrlHdr { hdr_type: u32, flags: u32, @@ -131,7 +131,7 @@ impl CtrlHdr for VirtioGpuCtrlHdr { } #[repr(C)] -#[derive(Default, Clone, Copy)] +#[derive(Default, Clone, Copy, Debug)] struct VirtioGpuRect { x_coord: u32, y_coord: u32, @@ -142,7 +142,7 @@ struct VirtioGpuRect { impl ByteCode for VirtioGpuRect {} #[repr(C)] -#[derive(Default, Clone, Copy)] +#[derive(Default, Clone, Copy, Debug)] struct VirtioGpuDisplayOne { rect: VirtioGpuRect, enabled: u32, @@ -152,7 +152,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], @@ -808,6 +808,7 @@ impl GpuIoHandler { } } drop(output_states_lock); + info!("virtio-gpu get the display info {:?}", display_info); self.send_response(req, &mut display_info) } @@ -1309,7 +1310,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); } @@ -1630,6 +1637,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(()) } @@ -1639,6 +1647,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 342bc03ea0c030b99206054540362e6a7ebd16a7 Mon Sep 17 00:00:00 2001 From: JinhaoGao Date: Tue, 24 Oct 2023 17:44:48 +0800 Subject: [PATCH 1398/1723] 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 Signed-off-by: Jinhao Gao --- 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 1e994a5dce4a4bcd622b1928c203a7d124931d0f Mon Sep 17 00:00:00 2001 From: JinhaoGao Date: Wed, 25 Oct 2023 03:41:03 +0800 Subject: [PATCH 1399/1723] 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 Signed-off-by: Jinhao Gao --- 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 042c4308dc52a9470267445f8c5783358d69bbc6 Mon Sep 17 00:00:00 2001 From: JinhaoGao Date: Wed, 25 Oct 2023 04:55:53 +0800 Subject: [PATCH 1400/1723] 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 Signed-off-by: Jinhao Gao --- 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 18a78dccd6dfa9c5d98bc7583abbd7892f34de62 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sat, 4 Nov 2023 17:24:18 +0800 Subject: [PATCH 1401/1723] 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 Signed-off-by: Jinhao Gao --- 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 fdee4f3217ce05a79ffcc55f013471cfc12d18e1 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 8 Nov 2023 17:26:26 +0800 Subject: [PATCH 1402/1723] 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 Signed-off-by: Jinhao Gao --- 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 2397952a04b19771331517bae8f803bb9842b2f1 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 23 Nov 2023 21:04:20 +0800 Subject: [PATCH 1403/1723] 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 16623852b..26edcefc7 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, }; @@ -1771,6 +1774,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 4afc90e9b..3e2a7e990 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 faa0192d010747d8bc087b44985ccd4b31ec73b5 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 23 Nov 2023 21:05:37 +0800 Subject: [PATCH 1404/1723] 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 26edcefc7..b52ac3444 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 3df44e3d86d8b56cbb34fc33d0b8e7b60c8d7189 Mon Sep 17 00:00:00 2001 From: prostous Date: Wed, 27 Sep 2023 21:29:25 +0300 Subject: [PATCH 1405/1723] 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 56a1e035d0b0bb59523f9aca55f677742947fa74 Mon Sep 17 00:00:00 2001 From: prostous Date: Wed, 27 Sep 2023 21:48:09 +0300 Subject: [PATCH 1406/1723] 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 1407/1723] 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 bc95394a87cd471e8946157a2c3a6635c74ab978 Mon Sep 17 00:00:00 2001 From: prostous Date: Wed, 27 Sep 2023 21:46:33 +0300 Subject: [PATCH 1408/1723] 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 e8f6a31f22fc8d80b08ffead208d29f9bb2eef34 Mon Sep 17 00:00:00 2001 From: prostous Date: Wed, 27 Sep 2023 20:50:40 +0300 Subject: [PATCH 1409/1723] 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 f69b87631c4ac988ed8df98750bfc4e67229d1fb Mon Sep 17 00:00:00 2001 From: prostous Date: Wed, 27 Sep 2023 21:55:48 +0300 Subject: [PATCH 1410/1723] 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 4cddab0ec480e2529b385d17ad732534fe2507c6 Mon Sep 17 00:00:00 2001 From: prostous Date: Thu, 28 Sep 2023 17:11:30 +0300 Subject: [PATCH 1411/1723] 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 c67a22a0c0b1e5e9efa2f4673bc1100114cbd905 Mon Sep 17 00:00:00 2001 From: prostous Date: Thu, 28 Sep 2023 20:21:22 +0300 Subject: [PATCH 1412/1723] 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 d9675e95b01818668c7215b15a2cebbb0fc479ae Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 19 Oct 2023 09:28:55 +0800 Subject: [PATCH 1413/1723] 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 6ef712de6763a2f9aa913e850611eeff957bd254 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Fri, 27 Oct 2023 23:06:11 +0800 Subject: [PATCH 1414/1723] 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 43ccadc143ccee679b474f873e154cb2ad6f49eb Mon Sep 17 00:00:00 2001 From: Dmitry Skorodumov Date: Fri, 27 Oct 2023 17:53:14 +0300 Subject: [PATCH 1415/1723] 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 d27d78b889099fa9ca1b43ad7872f6e5a32b595b Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 6 Sep 2023 15:28:32 +0800 Subject: [PATCH 1416/1723] 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 5a10ce421c685b21530c6e8eabc32fca3eb46366 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 6 Sep 2023 10:06:32 +0800 Subject: [PATCH 1417/1723] 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 31cc919bbf7ec870be5ceda0efcd51a57e08eec6 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 18 Oct 2023 15:23:05 +0800 Subject: [PATCH 1418/1723] 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 d7f9106cbc46e7249aa30e02585d572508a264c8 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 19 Sep 2023 22:09:38 +0800 Subject: [PATCH 1419/1723] 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 e6107bc1eb90b0baa34e62689a253719ee435771 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 10 Oct 2023 10:04:36 +0800 Subject: [PATCH 1420/1723] 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 48aba858d63a81333a5a899e699668c770638f41 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 10 Oct 2023 15:46:05 +0800 Subject: [PATCH 1421/1723] 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 a3a1170d5745d9eb000629c084d653e722f08081 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 10 Oct 2023 18:38:36 +0800 Subject: [PATCH 1422/1723] 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 67561f01a8308f189ed81d590a8a69ca63c132cf Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 29 Oct 2023 19:03:28 +0800 Subject: [PATCH 1423/1723] 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 da10558681fd68af59d4a75a1822e1186f91f3c5 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 29 Oct 2023 19:35:34 +0800 Subject: [PATCH 1424/1723] 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 57e2bd5a6dd9c68e1dd93c4e6e38a594fba56026 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 29 Oct 2023 21:03:29 +0800 Subject: [PATCH 1425/1723] 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 b3a510f007524899de8bf290afc70a97201abb3a Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 31 Oct 2023 10:25:37 +0800 Subject: [PATCH 1426/1723] 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 ce2fe5ac0..9dc9e713c 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -54,6 +54,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; @@ -91,8 +93,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 281e743b05796b53193565446ec5bc63ebe72426 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 31 Oct 2023 20:55:27 +0800 Subject: [PATCH 1427/1723] 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 47a071ec8ce602c96106eca2b81d002f235d3988 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 1 Nov 2023 08:57:44 +0800 Subject: [PATCH 1428/1723] 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 02d9e7b2fc12642db93bf4b55934899590122ae6 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 27 Oct 2023 11:27:42 +0800 Subject: [PATCH 1429/1723] 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 042250d95cf70553ce50a206ec680912f36f4a70 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 25 Oct 2023 04:55:53 +0800 Subject: [PATCH 1430/1723] 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 -- Gitee From 08164f6ae632b8a8df27c782e33eb4112a54c5bc Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 25 Oct 2023 14:51:35 +0800 Subject: [PATCH 1431/1723] 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 115d063178bce63c38123426d74f6354ac436377 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sun, 5 Nov 2023 22:50:09 +0800 Subject: [PATCH 1432/1723] 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 42a0b2fa17804389d467668986156033d2fea097 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 9 Nov 2023 09:13:01 +0800 Subject: [PATCH 1433/1723] 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 21aa0ea24d41a8f2495e70961c58fadae22b3b55 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 9 Nov 2023 09:17:56 +0800 Subject: [PATCH 1434/1723] 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 a3afd9c86122f957633268729129b8d4d865ec26 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Wed, 8 Nov 2023 15:04:30 +0800 Subject: [PATCH 1435/1723] 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 97c460bdaf6269a7e6ce89cdb5f875b90cecb544 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sat, 11 Nov 2023 11:22:35 +0800 Subject: [PATCH 1436/1723] 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 e297bb17d8aafb262a3da9392e2c7495d5ff17d3 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Wed, 8 Nov 2023 09:41:41 +0800 Subject: [PATCH 1437/1723] 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 4497f79764037972d648bd113023f2096f5847d8 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 11 Nov 2023 11:14:55 +0800 Subject: [PATCH 1438/1723] 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 9eb1e8bd9c63fb46de99fe3038f93de6607e7fb4 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 11 Nov 2023 11:26:15 +0800 Subject: [PATCH 1439/1723] 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 6274de73f0b6ae0fb7fc18b281afd0c1aaffb909 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 11 Nov 2023 11:58:36 +0800 Subject: [PATCH 1440/1723] 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 3d8df1e1794148f5372f5783bbdba6d12a3fdb1b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 12 Nov 2023 01:57:25 +0800 Subject: [PATCH 1441/1723] 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 b52ac3444..e6613bf6c 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -831,6 +831,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 cc71f5aed..b76607b1b 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1251,6 +1251,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 e4abaeb88818eb9b17291878851c96bf59e1fdb7 Mon Sep 17 00:00:00 2001 From: Huxiaohang Date: Wed, 8 Nov 2023 16:39:45 +0800 Subject: [PATCH 1442/1723] 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 3e2a7e990..18eee88e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -174,7 +174,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 } @@ -199,7 +199,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 } @@ -213,7 +213,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 64e9362033df14633754aa1ae472066c6620056c Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 14 Nov 2023 10:17:07 +0800 Subject: [PATCH 1443/1723] 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 c4f6efa00..7fc5b1d1b 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 b816bcac34f035dcc495a198f6a4df4164255a3e Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 14 Nov 2023 10:19:39 +0800 Subject: [PATCH 1444/1723] 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 7fc5b1d1b..8e6354854 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 8e1eb782359f9e29c9f387d9d9912181cda53263 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 16 Nov 2023 16:39:46 +0800 Subject: [PATCH 1445/1723] 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 3113ecefdb733183d7c81038e5c3ae136612c248 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 17 Nov 2023 15:08:36 +0800 Subject: [PATCH 1446/1723] 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 68ba7173c9d22463374b631c1811b9a6c9e81278 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 2 Nov 2023 10:36:50 +0800 Subject: [PATCH 1447/1723] 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 b9d00b3441b64ba0768f6e6abb0b07ba761435df Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 2 Nov 2023 20:16:54 +0800 Subject: [PATCH 1448/1723] 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 d83be1c949b5149b927e6f3a65bd02d1fae8ab31 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sun, 19 Nov 2023 15:19:26 +0800 Subject: [PATCH 1449/1723] 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 48fa9c9f7eefd8642e33fae78713cbc973a32c4f Mon Sep 17 00:00:00 2001 From: prostous Date: Mon, 2 Oct 2023 20:31:49 +0300 Subject: [PATCH 1450/1723] 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 0637b4aa900ff54e45b8d2898c94911da7162334 Mon Sep 17 00:00:00 2001 From: prostous Date: Wed, 4 Oct 2023 14:00:27 +0300 Subject: [PATCH 1451/1723] 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 b31843132dd454020f97252a074ff0ca339bd09d Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 19 Nov 2023 13:01:49 +0800 Subject: [PATCH 1452/1723] 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 1e4dbe6b4a070025ad4c66b4f5db51f261c7bb35 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sun, 19 Nov 2023 17:07:40 +0800 Subject: [PATCH 1453/1723] 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 5c8cbbd2d536cdc30fabde63951a995271ea9768 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 9 Oct 2023 11:03:51 +0800 Subject: [PATCH 1454/1723] 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 11a2b262cf61c433ab09f1846817ce2e92429f45 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 9 Oct 2023 20:16:37 +0800 Subject: [PATCH 1455/1723] 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 904841515c82841b0571955a45ba60865993ac25 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 13 Oct 2023 17:06:06 +0800 Subject: [PATCH 1456/1723] 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 157b7479c6e6accb39a36e5fba21a654d57648ba Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 13 Oct 2023 20:05:31 +0800 Subject: [PATCH 1457/1723] 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 c28d8480d985599dc300a31a95f860e1d67fa7aa Mon Sep 17 00:00:00 2001 From: prostous Date: Tue, 3 Oct 2023 12:17:38 +0300 Subject: [PATCH 1458/1723] 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 1c71f9694..7fc7915bd 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -384,13 +384,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 cbd178da1bdff1158780049d04308580d8b0eea5 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 19 Nov 2023 17:42:14 +0800 Subject: [PATCH 1459/1723] 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 46b7c5b6a532cb187168a999c61ae17cef38c644 Mon Sep 17 00:00:00 2001 From: Alisa Wang Date: Mon, 20 Nov 2023 19:12:29 +0800 Subject: [PATCH 1460/1723] 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 4b995ffde0dd08ebbe8de584d3d330b587d4c95a Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 11 Nov 2023 06:46:14 +0800 Subject: [PATCH 1461/1723] 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 4a4056513..207ff3407 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -112,8 +112,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 9a8c9b5540459fc06ce392b4b097b8d8485d6904 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 11 Nov 2023 04:33:46 +0800 Subject: [PATCH 1462/1723] 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 1fa50b2608e41961df8ba492eec2f304e2fd7b7a Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 11 Nov 2023 01:35:30 +0800 Subject: [PATCH 1463/1723] 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 b76607b1b..7129d2146 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); } @@ -884,15 +889,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!( @@ -984,22 +987,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 { @@ -1060,9 +1064,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, @@ -1070,17 +1075,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, @@ -1103,8 +1112,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) } @@ -1146,19 +1160,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 f30f40294c683770e632679df5d659c12b51cca8 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 21 Nov 2023 09:41:51 +0800 Subject: [PATCH 1464/1723] 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 4220c70dc..36b451516 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 e76a75c31f6ab16e1c19380ba50af030f816ab72 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 21 Nov 2023 15:59:05 +0800 Subject: [PATCH 1465/1723] 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 36b451516..c945c99cb 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 2d35dfdd31f95a445e26e60553b158e8c40f4598 Mon Sep 17 00:00:00 2001 From: li-huachao Date: Tue, 21 Nov 2023 16:44:47 +0800 Subject: [PATCH 1466/1723] 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 0856103562b1756083b93bfd3c6356a81f7175c8 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Wed, 22 Nov 2023 18:25:50 +0800 Subject: [PATCH 1467/1723] 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 3075153bdfd99569a8d600f65fe136f93a15a69f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 18 Nov 2023 18:19:28 +0800 Subject: [PATCH 1468/1723] 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 458cf756bbacc309b67ebb0fd18018f30fa19035 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 4 Nov 2023 18:39:44 +0800 Subject: [PATCH 1469/1723] 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 4e22cebe6406924202470e34084cdfe2e966c962 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 24 Nov 2023 09:19:52 +0800 Subject: [PATCH 1470/1723] 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 4eff9d8dc6d44abd71b1f9f6432fb5d0f926f0d7 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Fri, 24 Nov 2023 09:22:52 +0800 Subject: [PATCH 1471/1723] 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 623f4a3a0e51ce3ec49dc57ee97459198cbcf3cf Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 23 Sep 2023 12:32:26 +0800 Subject: [PATCH 1472/1723] 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 764c2ec93..e2c7de045 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 669cb83f6..a9c74ff8a 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}; @@ -2104,13 +2104,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 c92cfded39323cbbab6d5714dc66fd659877f84d Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 23 Sep 2023 17:51:59 +0800 Subject: [PATCH 1473/1723] 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 78cf88c25cc20676114311b4c2d2beca121db81f Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 23 Sep 2023 12:35:26 +0800 Subject: [PATCH 1474/1723] 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 a115ee285..0c403a73f 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 1e955dab6a36e5be701d32353a1b0cb08698651d Mon Sep 17 00:00:00 2001 From: dehengli Date: Sun, 26 Nov 2023 09:30:27 +0800 Subject: [PATCH 1475/1723] 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 8225860a7c76fd0f8cf184804af180b21fed8350 Mon Sep 17 00:00:00 2001 From: dehengli Date: Tue, 21 Nov 2023 09:36:00 +0800 Subject: [PATCH 1476/1723] 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 bc0b5ce4c989c3a54a6e7dd4f7a4c273e3fc757b Mon Sep 17 00:00:00 2001 From: dehengli Date: Tue, 28 Nov 2023 14:09:01 +0800 Subject: [PATCH 1477/1723] 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 0dc5cd075a816254a22ef7672df78f3666485d90 Mon Sep 17 00:00:00 2001 From: dehengli Date: Tue, 28 Nov 2023 13:56:39 +0800 Subject: [PATCH 1478/1723] 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 7bb1227aa1da92780bb298e49d75659a3063aab6 Mon Sep 17 00:00:00 2001 From: dehengli Date: Sat, 25 Nov 2023 23:25:53 +0800 Subject: [PATCH 1479/1723] 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 f07371a43c819387c4848dd060647f57fe34b52b Mon Sep 17 00:00:00 2001 From: dehengli Date: Tue, 28 Nov 2023 14:17:30 +0800 Subject: [PATCH 1480/1723] 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 e0175621fee7eb13366ffb901e8822a216677839 Mon Sep 17 00:00:00 2001 From: dehengli Date: Sat, 18 Nov 2023 20:17:57 +0800 Subject: [PATCH 1481/1723] 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 e54e842f95f9d03218edb44ff9e50f95cac03a0c Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 21 Nov 2023 06:03:38 +0800 Subject: [PATCH 1482/1723] 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 cf577648919ef546384b253130d6a668d5b1a115 Mon Sep 17 00:00:00 2001 From: wangmeiling Date: Thu, 16 Nov 2023 10:36:21 +0800 Subject: [PATCH 1483/1723] 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 660f68df0..43c592ba2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1678,6 +1678,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 bedb272e4..e6c3e2cd0 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 0f5b94340..de5fb9d2a 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -72,6 +72,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; @@ -1341,6 +1343,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 a094ad45e..b20e86a01 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), @@ -1065,6 +1066,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 9190d7d3b03d8ca2049a35b948de4959b6fffd45 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Mon, 27 Nov 2023 20:49:50 +0800 Subject: [PATCH 1484/1723] 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 0ad430f958c4543c44119718f2afbc83d2405548 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Mon, 27 Nov 2023 22:16:46 +0800 Subject: [PATCH 1485/1723] 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 de191481829ba0b56cd767611abe9fa1ce7389a1 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Fri, 24 Nov 2023 14:52:38 +0800 Subject: [PATCH 1486/1723] 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 672eb483e..1d1b8371d 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -352,6 +352,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, @@ -401,6 +402,7 @@ impl GtkDisplayScreen { window, dev_name, draw_area: DrawingArea::default(), + cursor_trsp: false, show_menu: RadioMenuItem::default(), source_surface: surface, transfer_surface: None, @@ -944,6 +946,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 5938ee5945e803a23624c8b5ceb5390627ec8cc3 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 27 Nov 2023 12:16:42 +0800 Subject: [PATCH 1487/1723] 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 93d53316353647afdf7af92bf519ad871b2fe6b4 Mon Sep 17 00:00:00 2001 From: li-huachao Date: Thu, 7 Dec 2023 09:23:15 +0800 Subject: [PATCH 1488/1723] 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 50ed40181ac90f31563f081422e9f26fedc6c7a1 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 21 Nov 2023 13:15:06 +0800 Subject: [PATCH 1489/1723] 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 c442b9b32c7bb0e1622be774f88e0add59a8df0f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 29 Nov 2023 03:49:52 +0800 Subject: [PATCH 1490/1723] 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 13f991e719bd45346874364349984838755cccba Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 23 Nov 2023 20:19:45 +0800 Subject: [PATCH 1491/1723] 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 9cb0dbdcacc45c1f8f97c5cb13dda646d29e5ac1 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 23 Nov 2023 20:20:22 +0800 Subject: [PATCH 1492/1723] 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 11137cb8c2f2453739740cd93a4af20cc928016a Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 28 Nov 2023 22:59:53 +0800 Subject: [PATCH 1493/1723] 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 6a1506d2f4cff2db40a20588702ade1f891a1549 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Fri, 8 Dec 2023 15:15:33 +0800 Subject: [PATCH 1494/1723] 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 c898aa14c9f3cce4aea17c8f124795732fcdeb53 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 24 Nov 2023 14:48:47 +0800 Subject: [PATCH 1495/1723] 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 ce5dd75d8..07ce2bd3c 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 8106849fb70133cc3a720ae57a33d73ccf89e117 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 7 Dec 2023 17:33:58 +0800 Subject: [PATCH 1496/1723] 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 07ce2bd3c..1dcdc8355 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 ba352186160300ce40c16c6785b91ab08a6459d8 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 9 Dec 2023 17:17:33 +0800 Subject: [PATCH 1497/1723] 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 4c6320e3cc6db88a851bf77d2b3404f4f8549245 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Sat, 9 Dec 2023 13:22:38 +0800 Subject: [PATCH 1498/1723] 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 f6388bc50597e7c54066df10b2de158cafe95cde Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 7 Dec 2023 21:47:50 +0800 Subject: [PATCH 1499/1723] 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 1fd7bf429f3addbeb46927ab6bfdc15e0a81e858 Mon Sep 17 00:00:00 2001 From: dehengli Date: Wed, 13 Dec 2023 13:45:36 +0800 Subject: [PATCH 1500/1723] 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 42ce2e2014569b0f19bcea301946aa59fadee003 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 13 Dec 2023 16:58:31 +0800 Subject: [PATCH 1501/1723] 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 c1536e3b7287d5182e04034aaf30d260e617c735 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Wed, 13 Dec 2023 20:58:01 +0800 Subject: [PATCH 1502/1723] 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 86258a690c66adc1de9809ed4ae4dc5500d38913 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 14 Dec 2023 21:14:08 +0800 Subject: [PATCH 1503/1723] 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 e0bf319b1dcd3cec9acec01179575ce885f00191 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 13 Dec 2023 06:04:03 +0800 Subject: [PATCH 1504/1723] 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 cfa3fb191ab3e440289033e8c27771df9f178366 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 13 Dec 2023 06:08:24 +0800 Subject: [PATCH 1505/1723] 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 b1a88d6776978025aeffee394053fb8a17e4a998 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 22 Nov 2023 20:00:58 +0800 Subject: [PATCH 1506/1723] 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 6f97a807320680a66eb5bfc9e9f3eff0ffd774f3 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 21 Nov 2023 22:20:14 +0800 Subject: [PATCH 1507/1723] 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 8ab52d69c14cfd74e976c9a701b080218bfa312d Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 22 Nov 2023 00:54:30 +0800 Subject: [PATCH 1508/1723] 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 c39249da41f43938296ce2a172c5834b61807b2b Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 22 Nov 2023 01:52:54 +0800 Subject: [PATCH 1509/1723] 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 8c394a64c4fe5a833d4f61e907c3a14ee9d202a0 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 22 Nov 2023 10:19:52 +0800 Subject: [PATCH 1510/1723] 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_common/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 58edb6b1c..373b797a1 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 7fbd309b3..54af9bbe0 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -190,6 +190,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_common/mod.rs b/machine/src/standard_common/mod.rs index 889506f23..fbe3fbba9 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -1782,6 +1782,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 9b27273d6b7dfe37dc968118259cfdd7ff16f1eb Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Wed, 20 Dec 2023 19:06:01 +0800 Subject: [PATCH 1511/1723] 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 bb161c9e0edbc74856ccf123d0229e9136c75a5a Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Sat, 9 Dec 2023 15:01:10 +0800 Subject: [PATCH 1512/1723] 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 06d002b7f39ab84c859f0226a9ba8b5fb1a60d6e Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Sat, 9 Dec 2023 17:03:03 +0800 Subject: [PATCH 1513/1723] 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 b0941f3e057e72650c8254a44dda91c94baf1eab Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Wed, 20 Dec 2023 19:06:54 +0800 Subject: [PATCH 1514/1723] 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 c93156149119ba00ffb1dc20fa12cd774b9b90ef Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 29 Dec 2023 08:52:40 +0800 Subject: [PATCH 1515/1723] 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 8f9c66756636f197d1a210706cbdf40b19fb10d6 Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Mon, 25 Dec 2023 19:04:06 +0800 Subject: [PATCH 1516/1723] 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 1b7e9ba19bbb83f2fdd2c2bbe190a2959410b789 Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Mon, 25 Dec 2023 19:26:28 +0800 Subject: [PATCH 1517/1723] 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 214a1ab55b702e6700e0a023544ef658b7c71727 Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Mon, 16 Oct 2023 23:02:33 +0800 Subject: [PATCH 1518/1723] 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 b87d5d89a..310042ddb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1534,6 +1534,7 @@ dependencies = [ "machine", "machine_manager", "thiserror", + "trace", "util", ] @@ -1675,6 +1676,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 802a9a6da..951638e8b 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 = [ @@ -34,6 +35,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 24f220ea463fa0d52df87e73d8461bdeddf02969 Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Mon, 16 Oct 2023 23:03:53 +0800 Subject: [PATCH 1519/1723] 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 310042ddb..ab162019e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -937,6 +937,7 @@ dependencies = [ "strum", "strum_macros", "thiserror", + "trace", "util", "vmm-sys-util", ] diff --git a/docs/qmp.md b/docs/qmp.md index 83fd67f10..4b0320cae 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 f74256492..8797af7a5 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 e3b049da7..fbfd7b033 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, }; @@ -586,9 +586,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 8363b519a..f262b657b 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -85,19 +85,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, }; @@ -612,16 +613,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`. @@ -797,27 +817,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 be368d1ac68f310a7f0b2d1cfcd5ce911876f0af Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Mon, 16 Oct 2023 23:05:51 +0800 Subject: [PATCH 1520/1723] 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 ab162019e..1830d3c48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -318,6 +318,7 @@ dependencies = [ "migration_derive", "nix 0.26.2", "thiserror", + "trace", "util", "vmm-sys-util", ] @@ -915,6 +916,7 @@ dependencies = [ "migration_derive", "serde_json", "thiserror", + "trace", "ui", "util", "vfio", @@ -1846,6 +1848,7 @@ dependencies = [ "once_cell", "serde_json", "thiserror", + "trace", "ui", "util", "vmm-sys-util", diff --git a/cpu/Cargo.toml b/cpu/Cargo.toml index 0cedc1eb3..1dccfcf54 100644 --- a/cpu/Cargo.toml +++ b/cpu/Cargo.toml @@ -20,6 +20,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 1406e30c2..ee2168d5f 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -268,7 +268,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!( @@ -839,10 +839,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 a9b37867c..081d2dc83 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 f62fb1e43..a2cf0bd3c 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 6080e822e..83fa7ce34 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(); @@ -1178,8 +1178,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 6790779410f5ec6a501184d70eb285ea2d9a24ba Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Wed, 3 Jan 2024 09:22:46 +0800 Subject: [PATCH 1521/1723] 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 03081470d282879ee534cb4d3c09784530452acd Mon Sep 17 00:00:00 2001 From: Alisa Wang Date: Tue, 9 Jan 2024 14:49:23 +0800 Subject: [PATCH 1522/1723] 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 fa320f786307db8a8182708677b1f78446ea39fc Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Wed, 3 Jan 2024 12:38:26 +0800 Subject: [PATCH 1523/1723] 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 de6dcf12fec7f8225d45764935b21690e3c2de32 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 10 Jan 2024 14:56:28 +0800 Subject: [PATCH 1524/1723] 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 482d3a812e59af4bae2884962dcf9ea878e52224 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 5 Jan 2024 16:31:04 +0800 Subject: [PATCH 1525/1723] 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 ee2168d5f..b2d7843c0 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 db28632be701f6ea327b1fc92c7c08bd4b0e26be Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sat, 13 Jan 2024 10:17:28 +0800 Subject: [PATCH 1526/1723] 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 3bf6c68a33d49404f7fa3aeb8b223ce7a6583a98 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 6 Jan 2024 16:46:47 +0800 Subject: [PATCH 1527/1723] 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 35af51977b0c24a9a85d3202e4062cdb97e88308 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 6 Jan 2024 19:52:30 +0800 Subject: [PATCH 1528/1723] 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 674d4e3fa6a3da42d5667f5d443de4a6dea099a1 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sun, 14 Jan 2024 22:35:47 +0800 Subject: [PATCH 1529/1723] 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 77538cd3039954727a64b0f0b3af61e4f0af8835 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 7 Jan 2024 00:20:42 +0800 Subject: [PATCH 1530/1723] 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 5fda2e03097827e6b13c6430a9476906b85e42e6 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 7 Jan 2024 07:36:33 +0800 Subject: [PATCH 1531/1723] 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 17d6eb2e65ae01a72450c699264086bf78d0b4a7 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 7 Jan 2024 07:56:15 +0800 Subject: [PATCH 1532/1723] 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 f703f2229f7e2834c4b66f815ff1589c84b2050a Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 9 Jan 2024 14:35:29 +0800 Subject: [PATCH 1533/1723] 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 3f38bfe3b29c8eca3baf4420552e0c26cc274cdb Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 9 Jan 2024 15:12:52 +0800 Subject: [PATCH 1534/1723] 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 d807380d45a90fdbbb3c1f28157ab22781142f24 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 9 Jan 2024 19:20:34 +0800 Subject: [PATCH 1535/1723] 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 b3a7f1c83bb5dbbf16374efefb68dd3a95d2175f Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 9 Jan 2024 19:34:10 +0800 Subject: [PATCH 1536/1723] 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 4f7e72f88bd329a2becbfa2f9aa0a52f16b63621 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 9 Jan 2024 21:57:43 +0800 Subject: [PATCH 1537/1723] 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 c47c130a3a77930daf9016627c4ba7c3c8edd069 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 10 Jan 2024 15:06:11 +0800 Subject: [PATCH 1538/1723] 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 624778f6fe1d2174a0e2ab66c2d352b7494c27b2 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 12 Jan 2024 08:56:36 +0800 Subject: [PATCH 1539/1723] 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 35eb445ffb0a8958353474454785c16dc483c3c4 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 23 Dec 2023 14:10:35 +0800 Subject: [PATCH 1540/1723] 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 22c49a1787fe519e0021f30f2042dfd9c1a80bd4 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 23 Dec 2023 17:09:15 +0800 Subject: [PATCH 1541/1723] 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 44b6ee3ac510178b4f6e94df9ba6e6e7fb08b5ba Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 23 Dec 2023 17:09:37 +0800 Subject: [PATCH 1542/1723] 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 09ed13aa3c358d92a2234e81bcd87d5f3fb391a2 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 23 Dec 2023 17:30:29 +0800 Subject: [PATCH 1543/1723] 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 ae361fd1aad9355b2be534e769fa48b5eb576628 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 23 Jan 2024 11:27:11 +0800 Subject: [PATCH 1544/1723] 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 539b00dd62589eea95d2e0c98b51341144640e3a Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 22 Jan 2024 12:23:32 +0800 Subject: [PATCH 1545/1723] 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 d12a347c81e0caf7f2b03583834ac56f4fa094b1 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 27 Jan 2024 14:57:50 +0800 Subject: [PATCH 1546/1723] 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 74999c7b17b1bea1e87f938351f8f8586d22493e Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sat, 6 Jan 2024 14:51:41 +0800 Subject: [PATCH 1547/1723] 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 95737b56c41218b36e75853e11ac0e934eb3f4f6 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Fri, 22 Dec 2023 17:19:36 +0800 Subject: [PATCH 1548/1723] 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 05e9fdd291da8bfb4d0b8c0152b01c6081cff607 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Fri, 22 Dec 2023 17:29:35 +0800 Subject: [PATCH 1549/1723] 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 3c0fde46fd70a7a2979c286737249dccf64c52d5 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 25 Dec 2023 15:47:11 +0800 Subject: [PATCH 1550/1723] 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 2af6e7f1439affb5c46a5277566beb7331489aa4 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 22 Jan 2024 20:41:47 +0800 Subject: [PATCH 1551/1723] 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 4d6772ce69fbe16503a46d7dc0f442a51809c38f Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 22 Jan 2024 20:57:37 +0800 Subject: [PATCH 1552/1723] 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 8c644f465403fe18c258693bd446232b61e7b3cb Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 22 Jan 2024 21:20:14 +0800 Subject: [PATCH 1553/1723] 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 4e4cc785dea3d313582ce8d318508c5f866ded9d Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 22 Jan 2024 21:36:44 +0800 Subject: [PATCH 1554/1723] 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 d6be07143b0b554af2dd9a78d04162efb7e6a33b Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 22 Jan 2024 22:26:35 +0800 Subject: [PATCH 1555/1723] 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 2cd5849c43a6df7cdf5a829d0b11d04eaffd6178 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 23 Jan 2024 11:01:35 +0800 Subject: [PATCH 1556/1723] 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 444d181565640ee892adce0d7d77ff5b2d48984f Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 23 Jan 2024 11:13:45 +0800 Subject: [PATCH 1557/1723] 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 d290451bf4333f7f201bec68a4bc1f85e3ff5c6d Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 23 Jan 2024 11:26:14 +0800 Subject: [PATCH 1558/1723] 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 969efb0bec348d6c03c83674bfe7a0d5020df1a7 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 3 Jan 2024 20:13:05 +0800 Subject: [PATCH 1559/1723] 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 fd4ed69777fb6ab3bdfe79a9766b57edbcb11b14 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Fri, 5 Jan 2024 09:16:21 +0800 Subject: [PATCH 1560/1723] 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 5154ffd3f561358bbec8cf48bb924bb125dce82a Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Fri, 5 Jan 2024 21:19:55 +0800 Subject: [PATCH 1561/1723] 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 ca1017c2465d7ee35c14f5d62c09aac8892e3674 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 8 Jan 2024 15:02:08 +0800 Subject: [PATCH 1562/1723] 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 0acb76fe5899c92513c396984f6a8e1beff2f0bc Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 8 Jan 2024 15:25:59 +0800 Subject: [PATCH 1563/1723] 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 002663d5fa73f9bcec1f80323e6dd46bd5e9b3f7 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 30 Jan 2024 15:05:11 +0800 Subject: [PATCH 1564/1723] 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 a3e4890b188959802d78aa49e9a34338c4b725e2 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 30 Jan 2024 06:19:53 +0800 Subject: [PATCH 1565/1723] 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 f01c5458d434db7eb3c8e5b5c2c187d410302815 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 29 Jan 2024 16:44:27 +0800 Subject: [PATCH 1566/1723] 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 31c89647c090c7cfa02dc197424f6a70fe5d8093 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 30 Jan 2024 03:58:05 +0800 Subject: [PATCH 1567/1723] 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 df24b14fb3b134fdd8a57f18558009e6b032a7b1 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Thu, 18 Jan 2024 11:19:29 +0800 Subject: [PATCH 1568/1723] 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 769894aa494c0cda23e8422c2bf5ff19a0dd5bdc Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 1 Feb 2024 18:06:40 +0800 Subject: [PATCH 1569/1723] 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 5ca8414e6a55cf60a102b9b1f19085eafc4b9ce2 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Thu, 1 Feb 2024 18:06:40 +0800 Subject: [PATCH 1570/1723] 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 b37cffc875f6c98ce9bc5c6bc9bb4d5a21ce24b7 Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Fri, 2 Feb 2024 16:59:20 +0800 Subject: [PATCH 1571/1723] PvPanic: remove name() and hotpluggable() remove name() and hotpluggable(), because their implementaion is functionally consistent with default trait implementation. Signed-off-by: boby.chen --- 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 514a47c00561b9c188fa461b9dca2490f56b2c52 Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Fri, 2 Feb 2024 17:55:26 +0800 Subject: [PATCH 1572/1723] 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: boby.chen --- 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 948e9f5ce0e56aed9872a987fdca357a3e741dad Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Fri, 2 Feb 2024 18:06:06 +0800 Subject: [PATCH 1573/1723] PvPanic: Add UT for PvPanic add UT for PvPanic device init and func Signed-off-by: boby.chen --- 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 9eefbdb1b..4a8d942d8 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 bus::PciBus; -- Gitee From de43147729e441ad2b29755aecc269abd1a4f9d4 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 1 Feb 2024 07:23:25 +0800 Subject: [PATCH 1574/1723] 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 289cef5163b93873273cdd73df97d64109533909 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 1 Feb 2024 06:42:16 +0800 Subject: [PATCH 1575/1723] 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 8f62e3d204677c19cd46ce13430b47e1430a9d30 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 Jan 2024 07:50:42 +0800 Subject: [PATCH 1576/1723] 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 06b82831538b05cbb544f1bafe50cffc4980d1b3 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 Jan 2024 07:37:25 +0800 Subject: [PATCH 1577/1723] 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 016022acdd9c0772fc067bb394bc06075dd6f2fa Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 Jan 2024 08:08:37 +0800 Subject: [PATCH 1578/1723] 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 388ae8f15578716362b886099b1b908308a1663b Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 Jan 2024 08:36:38 +0800 Subject: [PATCH 1579/1723] 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 ff6cad85b1e325531b0792d96c55bc4de097b3cd Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 22 Jan 2024 17:56:11 +0800 Subject: [PATCH 1580/1723] 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 5bff6354b732b720b8b7c6e4645243a7288647d1 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 22 Jan 2024 20:13:25 +0800 Subject: [PATCH 1581/1723] 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 6ccf1be60a46e57396b5adac98e952b836d7cf3a Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 22 Jan 2024 21:36:47 +0800 Subject: [PATCH 1582/1723] 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 55ec510684d3e0ab2c0bcc63bc0e39b5468a901a Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Mon, 5 Feb 2024 22:52:34 +0800 Subject: [PATCH 1583/1723] hypervisor/kvm: Upgrade kvm-ioctls version to 0.15.0 Signed-off-by: Fei Xu --- 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 a567f345f4ebc9949754f6bad12b9fa834794be0 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Tue, 6 Feb 2024 12:34:26 +0800 Subject: [PATCH 1584/1723] fmt: fix some cargo clippy errors error: casting to the same type is unnecessary Signed-off-by: Fei Xu --- 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 51d8777713e9125f35c1e4ed9c2a7a5b8d36250c Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 23 Jan 2024 12:11:34 +0800 Subject: [PATCH 1585/1723] 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 c5dd389c0e8a4147be237a2aae38aa59d6ae3283 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 1 Feb 2024 14:39:26 +0800 Subject: [PATCH 1586/1723] 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 23396d6288637df980b47570b817a48b15b7798a Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Wed, 31 Jan 2024 14:58:03 +0800 Subject: [PATCH 1587/1723] 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 3b71cf711..5ddecb423 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 d964215c0..f8e54be8a 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"] +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 c98cb4be0..79338fe0f 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 f257cf8e9..be02c636f 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -29,6 +29,7 @@ default = [] scream = [] scream_alsa = ["scream"] scream_pulseaudio = ["scream"] +scream_ohaudio = ["scream"] demo_device = [] usb_host = [] usb_camera = [] -- Gitee From 696f86cf7332beb8eb82340c6fde7309672b20b5 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Wed, 31 Jan 2024 14:58:33 +0800 Subject: [PATCH 1588/1723] 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 4c35967bc90efcedb3b0395ada842938368dd7cb Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 2 Feb 2024 07:39:33 +0800 Subject: [PATCH 1589/1723] 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 1f34e5e7b3aa8c49c5160f5a1c16e5888185a203 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 2 Feb 2024 07:57:32 +0800 Subject: [PATCH 1590/1723] 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 da3754ccd3315f9e0658202ca09ef353e5faba25 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 4 Feb 2024 20:57:41 +0800 Subject: [PATCH 1591/1723] 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 48219c022460e39216e7983d38f688768142f17f Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Sun, 4 Feb 2024 20:04:47 +0800 Subject: [PATCH 1592/1723] 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: Fei Xu --- 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 e392cb8ca85b564e9a7cd57957aaef5eaae4dc2b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 6 Feb 2024 13:29:42 +0800 Subject: [PATCH 1593/1723] 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 9b4d9a331a425969055abd31ee3b7451c014dda7 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Tue, 6 Feb 2024 14:38:29 +0800 Subject: [PATCH 1594/1723] deps: Clean up dependency to KVM related crates Signed-off-by: Fei Xu --- 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 f8e54be8a..b56665f1f 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 79338fe0f..f8470ea8c 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 177366441..a76e09e45 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 be02c636f..32c49c20a 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 53af72725a7aa11514d1bbc82e162c1bb3f85574 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 7 Feb 2024 12:15:06 +0800 Subject: [PATCH 1595/1723] scream: bugfix: supplement missing "Default" trait missing derive Default trait for samplesize. Signed-off-by: Fei Xu --- 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 c4da014f01768054441d85187c38582e620e1584 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 7 Feb 2024 16:00:50 +0800 Subject: [PATCH 1596/1723] 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 39d7801d551bc0f18ce66fec9d47faf92bf2462d Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 5 Feb 2024 23:28:33 +0800 Subject: [PATCH 1597/1723] 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 202a712e2de4ddffd017277a1b475d375edc58f1 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 6 Feb 2024 00:13:30 +0800 Subject: [PATCH 1598/1723] 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 8a56bbd4692bce584b21faf78f5f3afb231ecfc1 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 23 Jan 2024 21:39:36 +0800 Subject: [PATCH 1599/1723] 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 7234dd15bb775b4b59559048dff7229fd6a418ae Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 22 Jan 2024 19:53:01 +0800 Subject: [PATCH 1600/1723] 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 c862cbac0ceec2a0542fc5b417b40c132a6201d9 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 31 Jan 2024 20:12:16 +0800 Subject: [PATCH 1601/1723] 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 92f6d33ea5f2eea8c3ea66b9da8d1e96e105c73f Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 19 Feb 2024 21:35:34 +0800 Subject: [PATCH 1602/1723] 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 b0fb3328d4a9399a058ab301bfc1291a599250ae Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 19 Feb 2024 10:22:17 +0800 Subject: [PATCH 1603/1723] 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 fa984b8071c0c129693f4a3994017ef2f275d112 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 19 Feb 2024 10:23:39 +0800 Subject: [PATCH 1604/1723] 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 8c3422ef9a332cfc2661a8ab43e2a184e5edc4f0 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 19 Feb 2024 10:24:43 +0800 Subject: [PATCH 1605/1723] 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 8204aaa5f04c353405feb2f44ec675fb3bb97ba1 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 15 Jan 2024 16:53:38 +0800 Subject: [PATCH 1606/1723] 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 ca40cb414..2b25d798f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -272,6 +272,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" @@ -331,6 +365,7 @@ dependencies = [ "byteorder", "cairo-rs", "chardev_backend", + "clap", "cpu", "drm-fourcc", "libc", @@ -904,6 +939,7 @@ dependencies = [ "anyhow", "block_backend", "boot_loader", + "clap", "cpu", "devices", "hypervisor", @@ -1127,6 +1163,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 f1b01ec5f622010d403ec9e03bab4b7e7ec5f4f3 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 16 Jan 2024 19:04:37 +0800 Subject: [PATCH 1607/1723] 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 2b25d798f..f42f72efb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1877,6 +1877,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 67855d645743d07e313e9c7fea7eadfbfa95d2f6 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 17 Jan 2024 19:09:39 +0800 Subject: [PATCH 1608/1723] 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 f42f72efb..324b69ae2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -963,6 +963,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 90e3fc1048645e52787620e477cf2778ee4d3627 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 19 Jan 2024 12:30:35 +0800 Subject: [PATCH 1609/1723] 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 53d50b4bc..4e605faa8 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, }; @@ -1288,7 +1288,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 6f841bab4cdfa5ecd9dbf4087a52dca3ae717742 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 19 Jan 2024 14:50:07 +0800 Subject: [PATCH 1610/1723] 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 bed69ff3da0fe8ec5d967d33e678c84a26566265 Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Tue, 20 Feb 2024 16:35:20 +0800 Subject: [PATCH 1611/1723] 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 1fdd37d204e071c4db779205a809f91a7e83d20f Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 19 Feb 2024 16:42:47 +0800 Subject: [PATCH 1612/1723] 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 5edbafc3575967f5cf9a0c4a43c6b10fc07c8132 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 21 Feb 2024 23:09:58 +0800 Subject: [PATCH 1613/1723] 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 1e185164785dbfb4afc366060458d2eefcfdb99d Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 1 Feb 2024 15:02:38 +0800 Subject: [PATCH 1614/1723] 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 e90f891db28a3d4e99ec53c0703967025031c228 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Wed, 31 Jan 2024 20:30:27 +0800 Subject: [PATCH 1615/1723] 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 7439463b63a7bdc406d8b4d0461a2ec975138c73 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Sun, 4 Feb 2024 14:30:18 +0800 Subject: [PATCH 1616/1723] 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 8a76123200c56ad08864fefe94650a1d7390f0fd Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 1 Feb 2024 15:10:43 +0800 Subject: [PATCH 1617/1723] 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 059d8a6db..4399c4e7c 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(); @@ -575,6 +606,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")?; @@ -662,8 +696,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(), @@ -675,6 +710,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 ddaec6c7a..08ab2afed 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, @@ -1352,12 +1356,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 68a77a9ee74775cd7cbe57b1b900cce69c9d3d9f Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 22 Feb 2024 09:33:46 +0800 Subject: [PATCH 1618/1723] 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 24614f51a..263dd6318 100644 --- a/hypervisor/src/kvm/aarch64/mod.rs +++ b/hypervisor/src/kvm/aarch64/mod.rs @@ -398,11 +398,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 4399c4e7c..d50237e09 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -216,13 +216,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 25d147e65..e8882d131 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 eb4bba91290f9516960788fcf74f150f98976ada Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 8 Feb 2024 12:35:31 +0800 Subject: [PATCH 1619/1723] 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 2099b3bb4..be0ab375d 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 263dd6318..d15dd3d6d 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 e43958b2d..e11f18393 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 7b115041826013992e9ddb251e06932267f34b9c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 8 Feb 2024 13:10:30 +0800 Subject: [PATCH 1620/1723] 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 d15dd3d6d..010499a82 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 a06ecd3ff7f36d888477ff85922c09dfafb37390 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 22 Feb 2024 21:52:25 +0800 Subject: [PATCH 1621/1723] 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 6bba93c9fbf8dcbfbe4c2a7cadc37aa7a5b6c4e4 Mon Sep 17 00:00:00 2001 From: Dmitry Skorodumov Date: Wed, 14 Feb 2024 21:42:57 +0300 Subject: [PATCH 1622/1723] 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 5d172a0f7..8b67ccb39 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 317cbf8451a5d4ec94bb12fe8456a4d920fc0ede Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Wed, 21 Feb 2024 13:16:14 +0800 Subject: [PATCH 1623/1723] 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 e683d62f0992416729cfe353eb989c06313fe361 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Wed, 21 Feb 2024 13:39:51 +0800 Subject: [PATCH 1624/1723] 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 be86f8812bd3d0766f3f66a42ceb2baa762356c8 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Fri, 23 Feb 2024 11:28:49 +0800 Subject: [PATCH 1625/1723] 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 0409cfecd204c01ec648b8bab9675f47377bbfdc Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Fri, 23 Feb 2024 16:57:57 +0800 Subject: [PATCH 1626/1723] 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 1f8d9b35e5e0c64f41a6f1a7b8b7901ba53ca9dc Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 26 Feb 2024 09:15:17 +0800 Subject: [PATCH 1627/1723] 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 2c1000e4f9997ded67e7077180a962de97f0d2fa Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Tue, 27 Feb 2024 16:31:07 +0800 Subject: [PATCH 1628/1723] 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 c6a819b08d87ed0b388820adfd281fb0b0d5a0ae Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 27 Feb 2024 19:20:26 +0800 Subject: [PATCH 1629/1723] 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 91846a50cc32e7d58cce2e8e0dfde22de91783cc Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Wed, 7 Feb 2024 09:38:43 +0800 Subject: [PATCH 1630/1723] PvPanic: Add MST for PvPanic add MST for PvPanic device Signed-off-by: boby.chen --- 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 327e0a82c11f482169b167b8c7b346eb7a967f16 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 29 Feb 2024 11:32:26 +0800 Subject: [PATCH 1631/1723] 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 eb2f924903ab3d458b238fb8fb594fc7954056a5 Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Thu, 29 Feb 2024 17:54:21 +0800 Subject: [PATCH 1632/1723] 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 2a9c44a9c57a6cfae45dab2d64a8592ac5177567 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 25 Jan 2024 20:29:50 +0800 Subject: [PATCH 1633/1723] 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 7cef00855..5ba759838 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -808,6 +808,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 @@ -827,6 +829,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 6c1b7eeb191aa34a359c934f4f60dfbfe634e55f Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sun, 18 Feb 2024 10:30:03 +0800 Subject: [PATCH 1634/1723] 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 4472cd92e7e5723af88c84b3079b40483ea9dc26 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sun, 18 Feb 2024 14:14:00 +0800 Subject: [PATCH 1635/1723] 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 5ba759838..e36a22223 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -925,7 +925,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. @@ -937,6 +937,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); @@ -945,6 +947,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 @@ -953,6 +959,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 8ab4c8f4df04f05de9370420bcce93c2f88381ea Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 19 Feb 2024 10:06:34 +0800 Subject: [PATCH 1636/1723] 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 b5ebdb590de1777e4ee0259aae3be0277b6ddc3e Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sun, 18 Feb 2024 10:48:38 +0800 Subject: [PATCH 1637/1723] 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 33458c9b5..a8d75f4fb 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 178f4aefc296a5d65ba9255d4c19a4c07d84a75c Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sun, 18 Feb 2024 11:05:03 +0800 Subject: [PATCH 1638/1723] 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 a8d75f4fb..43d7d1083 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 ec858389d68a6c1b5d6d1ccca8fcfc887602d02f Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sun, 18 Feb 2024 11:15:21 +0800 Subject: [PATCH 1639/1723] 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 43d7d1083..b7638f9d9 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 f868b61f8242cae2b8e7335c16f7e0876207973c Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 19 Feb 2024 11:23:45 +0800 Subject: [PATCH 1640/1723] 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 e1c47a4b1cbc66e88d106ca2579b31cf3637259b Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 26 Jan 2024 09:32:34 +0800 Subject: [PATCH 1641/1723] 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 b7638f9d9..850fe06ed 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 e3857c64e52252c35d0394fa7f0c0159bd12c935 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 23 Feb 2024 11:20:28 +0800 Subject: [PATCH 1642/1723] 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 f3cbae1f5dfba1d800e50d972c2e630f68269761 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 29 Feb 2024 15:14:29 +0800 Subject: [PATCH 1643/1723] 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 098f69e3ffaad16f61e06f52f7ac17afa75fc4f5 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 29 Feb 2024 19:26:04 +0800 Subject: [PATCH 1644/1723] 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 fd5973dc6029a628dadc24b5966a90afa754f77b Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 4 Mar 2024 15:50:38 +0800 Subject: [PATCH 1645/1723] 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 ec05dbafdb655f784a9100de38d6c0cc33a0c089 Mon Sep 17 00:00:00 2001 From: "boby.chen" Date: Wed, 6 Mar 2024 11:12:34 +0800 Subject: [PATCH 1646/1723] Build: Delete redundant dynamic library refereces. Delete redundant dynamic library refereces. Signed-off-by: boby.chen --- 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 30c2666dcc45818d15c016e7421a84e448563b9b Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 4 Mar 2024 22:38:02 +0800 Subject: [PATCH 1647/1723] 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 608d8bb10536739df1eb4728f6eb38dd11f443ff Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 2 Mar 2024 06:12:53 +0800 Subject: [PATCH 1648/1723] 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 d5c79e6000a03daae113540982977cfe69b7ab2b Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 3 Mar 2024 06:42:26 +0800 Subject: [PATCH 1649/1723] 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 22f3aa10cfb9c78e960b36a091f8fa724185479f Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 12 Mar 2024 11:30:54 +0800 Subject: [PATCH 1650/1723] 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 edbfd4f5c9c28b492f4c06101bbd0aca5c3fd73e Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sun, 3 Mar 2024 22:36:18 +0800 Subject: [PATCH 1651/1723] 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 0f19f52412621088752f43545205fef5efdb1457 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 22 Feb 2024 13:46:52 +0800 Subject: [PATCH 1652/1723] 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 efcfa4090409c48efb00426a10655126807c701a Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sat, 9 Mar 2024 17:45:15 +0800 Subject: [PATCH 1653/1723] 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 1062bf2c880d3e73a7eb4dcc96e6e4fff3a4c307 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 5 Mar 2024 21:30:25 +0800 Subject: [PATCH 1654/1723] 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 bc7df8020fed9bcda42fedde072516b69ec24e7b Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 22 Feb 2024 16:18:58 +0800 Subject: [PATCH 1655/1723] 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 be42d142b8e4786bf68f919e78252d6da5552f06 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 22 Feb 2024 16:28:59 +0800 Subject: [PATCH 1656/1723] 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 c01bbeff3c7185ad7564162ddd56a35df8954124 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 22 Feb 2024 17:48:34 +0800 Subject: [PATCH 1657/1723] 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 6ed248270ffdbfb274a5c2afec108a28049b607a Mon Sep 17 00:00:00 2001 From: Huxiaohang Date: Tue, 12 Mar 2024 19:43:14 +0800 Subject: [PATCH 1658/1723] 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 4e05572fd082e2847f1599edf51430a255b5da07 Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Wed, 13 Mar 2024 19:23:43 +0800 Subject: [PATCH 1659/1723] 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 52be66b8d45745641b28f0ca664273c10aa418c8 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 24 Feb 2024 00:12:41 +0800 Subject: [PATCH 1660/1723] 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 7db507eb4486c54b106d1fecfdec49da0727e8a0 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Tue, 12 Mar 2024 20:19:57 +0800 Subject: [PATCH 1661/1723] 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 9cd65cd9ef74526e79baf7b248747604b081bed8 Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Thu, 14 Mar 2024 12:32:05 +0800 Subject: [PATCH 1662/1723] 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 f14e33e45f42290753a9610f8931a7839a550c8d Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Fri, 15 Mar 2024 09:44:04 +0800 Subject: [PATCH 1663/1723] 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 462ffe2427a43a2108255e6d3e3ab6a51c4a7a50 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 15 Mar 2024 10:47:27 +0800 Subject: [PATCH 1664/1723] 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 61a7a73e20906d7fea8a01797539aa1e21fa6dd8 Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Fri, 15 Mar 2024 14:11:05 +0800 Subject: [PATCH 1665/1723] 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 4d00f628aae436bba58167c253bf2dde67b66f4f Mon Sep 17 00:00:00 2001 From: YeXiao Date: Fri, 15 Mar 2024 14:54:32 +0800 Subject: [PATCH 1666/1723] 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 3b1f985ab7539b9f1b5041d09c2015e85d41b93c Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 11 Mar 2024 16:13:24 +0800 Subject: [PATCH 1667/1723] 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 cc26d3bf9e3d5a65500ee21cba3476226b728cff Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 11 Mar 2024 16:25:53 +0800 Subject: [PATCH 1668/1723] 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 d782e3d5c..24dc5dcbe 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -71,7 +71,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, @@ -128,7 +128,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, @@ -202,6 +202,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 7e8c86a990d05e8ed86629463ffbde4eed21df11 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 11 Mar 2024 22:00:28 +0800 Subject: [PATCH 1669/1723] 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 6978dcf0a3e26cc58025d48513b82414a4e5e295 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 17 Mar 2024 23:21:07 +0800 Subject: [PATCH 1670/1723] 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 98a4b92bc7cf28e0919356b76fa1541ece806800 Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Tue, 19 Mar 2024 20:18:20 +0800 Subject: [PATCH 1671/1723] 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 13917f1b6393eecf612de57d210ca0b3c99743d4 Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Tue, 19 Mar 2024 20:18:57 +0800 Subject: [PATCH 1672/1723] 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 3618346a5828807af1b5f73bbdc310e6515c90c1 Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Tue, 19 Mar 2024 20:19:22 +0800 Subject: [PATCH 1673/1723] 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 0642e1ed7222cdd9f6442b77cad4da4687f78f48 Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Tue, 19 Mar 2024 20:21:33 +0800 Subject: [PATCH 1674/1723] 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 b3fd1c464686c597e6377777674405dc96f63ae8 Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Tue, 19 Mar 2024 20:21:38 +0800 Subject: [PATCH 1675/1723] 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 884dba97f5124d69690d644e9677af9cb4aaca3e Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 20 Mar 2024 12:45:33 +0800 Subject: [PATCH 1676/1723] 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 68d3d6cc2edf871d80d45e221a407a38e5c8ebbc Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Tue, 12 Mar 2024 20:19:57 +0800 Subject: [PATCH 1677/1723] 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 7d068226d3583614bf89cb1ba28f4ce7d582fa67 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 20 Mar 2024 17:27:00 +0800 Subject: [PATCH 1678/1723] 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 5bb26b328ac138136b56210a0625fff44ce4c93f Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Mon, 25 Mar 2024 17:00:17 +0800 Subject: [PATCH 1679/1723] 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 212dff8ee28e864dcd752e7ad7ce368978b8f03b Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Mon, 25 Mar 2024 15:36:26 +0800 Subject: [PATCH 1680/1723] 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 46fa534a61e58de4986a592d5f86630ca6a752cd Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 26 Mar 2024 21:12:07 +0800 Subject: [PATCH 1681/1723] 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 62babf2b38ed64797d7db3076756de99ef756a22 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Wed, 27 Mar 2024 20:21:08 +0800 Subject: [PATCH 1682/1723] 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 066ed36d74cd67b7b6ab6d9dfc37a69f687baa1f Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 28 Mar 2024 11:05:01 +0800 Subject: [PATCH 1683/1723] 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 2fa5256a4957c8ee265b38c4a2f086c2bc88ed6d Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 28 Mar 2024 09:33:19 +0800 Subject: [PATCH 1684/1723] 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 e97ad06101afa08a5d2c28b68ce93b8815aa6a1e Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Fri, 29 Mar 2024 14:25:46 +0800 Subject: [PATCH 1685/1723] 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 7b83bd91426cfbbbc2bfb59c44df03e8284e2e9b 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 1686/1723] 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 8499ec8b35d9b273770155729817043d9502e769 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Sun, 7 Apr 2024 10:10:04 +0800 Subject: [PATCH 1687/1723] 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 4ff29818dc1a8d4beccdd4e7017d94e45488c5c9 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Wed, 27 Mar 2024 12:03:24 +0800 Subject: [PATCH 1688/1723] 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 6e106f7e53f40a3fb8f7dfa160e71e87b91d4831 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Mon, 1 Apr 2024 15:24:16 +0800 Subject: [PATCH 1689/1723] 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 b3d94ee847326fa779de035d666857afee7b6f28 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 10 Apr 2024 19:36:12 +0800 Subject: [PATCH 1690/1723] 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 e3a9d254ef311eacb9b743359d57e7ea2b93b29f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 11 Apr 2024 09:12:53 +0800 Subject: [PATCH 1691/1723] 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 3032f487e458e2620cd52b024301dfd61fe266a2 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 11 Apr 2024 09:24:28 +0800 Subject: [PATCH 1692/1723] 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 c13a9a7baa349413004d88a74018796c43f026e7 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 11 Apr 2024 11:06:07 +0800 Subject: [PATCH 1693/1723] 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 872afd772fdcbb8f51cc527262f9778c7eb53cf7 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 11 Apr 2024 11:16:16 +0800 Subject: [PATCH 1694/1723] 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 18ea7ca010bb801171d1c0444d14b489b9115d67 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 24 Mar 2024 10:49:30 +0800 Subject: [PATCH 1695/1723] 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 be7575fcd5acd8544fcc4532363625e7c265e694 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 29 Mar 2024 18:54:33 +0800 Subject: [PATCH 1696/1723] 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 f34585e696ef671a1fe080934111ee6e147faae0 Mon Sep 17 00:00:00 2001 From: dskr99 Date: Mon, 4 Mar 2024 12:50:47 +0300 Subject: [PATCH 1697/1723] 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 f880e7817bfedeafd8d17943be5d58d8a6a694e8 Mon Sep 17 00:00:00 2001 From: dskr99 Date: Mon, 4 Mar 2024 17:10:16 +0300 Subject: [PATCH 1698/1723] 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 df74d0895b315ff36b78918df3c0830485aba0ea Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Mon, 15 Apr 2024 11:09:56 +0800 Subject: [PATCH 1699/1723] 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 22238777e2aac1198887ea9a0ae33176c44be4f1 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 1700/1723] 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 5408ca568ba01db04e7c8fb17df3b3834d66bb10 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 15 Apr 2024 14:42:07 +0800 Subject: [PATCH 1701/1723] 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 a121ba17f7ad1ad7144ec4ca40058bbb4b9918cb Mon Sep 17 00:00:00 2001 From: Huxiaohang Date: Tue, 16 Apr 2024 17:53:02 +0800 Subject: [PATCH 1702/1723] 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 9f297392bced5cf0dce424c6880bfa2fd9169e69 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 13 Apr 2024 06:15:20 +0800 Subject: [PATCH 1703/1723] 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 eb1ac51232f18eddaed65b1f3ff9e3d591df91c1 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Mon, 15 Apr 2024 19:31:52 +0800 Subject: [PATCH 1704/1723] 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 6227f8e93f4596bd6fcdf5f0bc8a9bd611edb59d Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Fri, 19 Apr 2024 14:31:35 +0800 Subject: [PATCH 1705/1723] 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 35068675953bc003b3dc94fd1355091f801bd489 Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Sun, 24 Mar 2024 16:30:12 +0800 Subject: [PATCH 1706/1723] 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 4a3e3c64c85d3de8445f1d8d95ca4e1f50adeda7 Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Sun, 24 Mar 2024 18:07:50 +0800 Subject: [PATCH 1707/1723] 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 8e14d8c2e..5b0d2751e 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 e54bba416..4f4f5f95c 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. @@ -1934,7 +1934,7 @@ pub struct DeviceDeleted { pub path: String, } -/// trace-event-get-state +/// trace-get-state /// /// # Arguments /// @@ -1943,25 +1943,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 /// @@ -1971,20 +1971,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 a5f7a21ff5907631c767d209615f2d47ae825b11 Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Sun, 24 Mar 2024 18:12:44 +0800 Subject: [PATCH 1708/1723] 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 ea9b863a8..e4837f587 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -534,34 +534,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 2abd3c98077a98ca5023907a0f75c6c6c0a3a04a Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Sat, 20 Apr 2024 16:24:36 +0800 Subject: [PATCH 1709/1723] 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 b9eada7db276a7ba85c59deea981ad3547824b4d Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Sat, 20 Apr 2024 19:05:12 +0800 Subject: [PATCH 1710/1723] 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 6ce3237f72a02028aa91b2625ea746b4b9b93289 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Mon, 22 Apr 2024 21:16:14 +0800 Subject: [PATCH 1711/1723] 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 67d8074c96d3c8b887a25705f203508dfb790c58 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sun, 21 Apr 2024 22:04:30 +0800 Subject: [PATCH 1712/1723] 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 f59090011..d8fb92e61 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -225,10 +225,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 5ed101beb546fa9b6a3e720840a95b109b2ba21a Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 23 Apr 2024 14:48:33 +0800 Subject: [PATCH 1713/1723] 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 3da266956a75eb3aab3bd44d5c41e47d4bec59ca Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 30 Mar 2024 00:20:00 +0800 Subject: [PATCH 1714/1723] 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 4d47aabf02116953241b64fa2eb78cda43b8d68f Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 31 Mar 2024 10:27:29 +0800 Subject: [PATCH 1715/1723] 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 01ac847eb8515d504c2a54c4c12171a872210bf9 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 31 Mar 2024 10:33:15 +0800 Subject: [PATCH 1716/1723] 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 b65279aff407a1eed256ca4c967be1f76236bf97 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 22 Apr 2024 22:50:01 +0800 Subject: [PATCH 1717/1723] 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 d3b32b757a5221df5c3af658e465361614bcc89b Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 31 Mar 2024 10:37:15 +0800 Subject: [PATCH 1718/1723] 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 92c230fe42991046be91c89435a46b69a056c037 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 29 Mar 2024 11:57:45 +0800 Subject: [PATCH 1719/1723] 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 d89d858815ca65d85615c121b83bc6df57c07c05 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 31 Mar 2024 13:06:13 +0800 Subject: [PATCH 1720/1723] 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 10068e7a26628218af3d7e3281f9d239045ebc80 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 16 Apr 2024 19:25:19 +0800 Subject: [PATCH 1721/1723] 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 058fe69dab0ac7f0a9b64701abb473f0e5183fd6 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 10 May 2024 15:21:31 +0800 Subject: [PATCH 1722/1723] 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 fc8729308..fba3e56a1 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", @@ -751,7 +751,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hypervisor" -version = "2.3.0" +version = "2.4.0" dependencies = [ "address_space", "anyhow", @@ -934,7 +934,7 @@ checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" [[package]] name = "machine" -version = "2.3.0" +version = "2.4.0" dependencies = [ "acpi", "address_space", @@ -962,7 +962,7 @@ dependencies = [ [[package]] name = "machine_manager" -version = "2.3.0" +version = "2.4.0" dependencies = [ "anyhow", "clap", @@ -1016,7 +1016,7 @@ dependencies = [ [[package]] name = "migration" -version = "2.3.0" +version = "2.4.0" dependencies = [ "anyhow", "kvm-ioctls", @@ -1032,7 +1032,7 @@ dependencies = [ [[package]] name = "migration_derive" -version = "2.3.0" +version = "2.4.0" dependencies = [ "migration", "proc-macro2", @@ -1049,7 +1049,7 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "mod_test" -version = "2.3.0" +version = "2.4.0" dependencies = [ "acpi", "anyhow", @@ -1175,7 +1175,7 @@ checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" [[package]] name = "ozone" -version = "2.3.0" +version = "2.4.0" dependencies = [ "anyhow", "libc", @@ -1572,7 +1572,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stratovirt" -version = "2.3.0" +version = "2.4.0" dependencies = [ "anyhow", "log", @@ -1585,7 +1585,7 @@ dependencies = [ [[package]] name = "stratovirt-img" -version = "2.3.0" +version = "2.4.0" dependencies = [ "anyhow", "block_backend", @@ -1723,7 +1723,7 @@ dependencies = [ [[package]] name = "trace" -version = "2.3.0" +version = "2.4.0" dependencies = [ "anyhow", "lazy_static", @@ -1735,7 +1735,7 @@ dependencies = [ [[package]] name = "trace_generator" -version = "2.3.0" +version = "2.4.0" dependencies = [ "proc-macro2", "quote", @@ -1746,7 +1746,7 @@ dependencies = [ [[package]] name = "ui" -version = "2.3.0" +version = "2.4.0" dependencies = [ "address_space", "anyhow", @@ -1801,7 +1801,7 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "util" -version = "2.3.0" +version = "2.4.0" dependencies = [ "anyhow", "arc-swap", @@ -1849,7 +1849,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vfio" -version = "2.3.0" +version = "2.4.0" dependencies = [ "address_space", "anyhow", @@ -1876,7 +1876,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 95ca790d1..316752abe 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 55557a675af6b46c02e9d7d7154a3fd8829d15f8 Mon Sep 17 00:00:00 2001 From: lixiang Date: Mon, 14 Oct 2024 09:24:07 +0800 Subject: [PATCH 1723/1723] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devices/src/misc/scream/pulseaudio.rs | 2 +- docs/snapshot.md | 2 +- tests/hydropper/utils/utils_logging.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/devices/src/misc/scream/pulseaudio.rs b/devices/src/misc/scream/pulseaudio.rs index c42fabacd..4cac7dac7 100644 --- a/devices/src/misc/scream/pulseaudio.rs +++ b/devices/src/misc/scream/pulseaudio.rs @@ -352,7 +352,7 @@ mod tests { assert_eq!(pulse.ss.rate, AUDIO_SAMPLE_RATE_48KHZ); assert_eq!(pulse.ss.format, Format::S24le); - // Settint invalid sample size to 100. + // Setting invalid sample size to 100. test_data.fmt.size = 100; pulse.check_fmt_update(&test_data); 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":[]}} diff --git a/tests/hydropper/utils/utils_logging.py b/tests/hydropper/utils/utils_logging.py index 6c60200ea..6f5bf4052 100644 --- a/tests/hydropper/utils/utils_logging.py +++ b/tests/hydropper/utils/utils_logging.py @@ -80,7 +80,7 @@ class Logger(): # FileHandler log level self.f_level = level[1] - self.msg = "Hello Word, just test" + self.msg = "Hello World, just test" if not mode: self.mode = r'a' -- Gitee