From 04c30f700a232e705ab3b62bec85dec273fd7065 Mon Sep 17 00:00:00 2001 From: Xuewei Niu Date: Tue, 11 Feb 2025 18:13:46 +0800 Subject: [PATCH] vhost-device-console: Being able to specify max queue size Some hypervisors, e.g. UML, negotiate a queue size larger than `QUEUE_SIZE` (128), which results in failures. This patch allows users to specify it using `--max-queue-size `. Signed-off-by: Xuewei Niu --- vhost-device-console/CHANGELOG.md | 2 + vhost-device-console/README.md | 5 ++ vhost-device-console/src/backend.rs | 26 ++++++++-- vhost-device-console/src/main.rs | 8 +++ vhost-device-console/src/vhu_console.rs | 68 +++++++++++++++---------- 5 files changed, 77 insertions(+), 32 deletions(-) diff --git a/vhost-device-console/CHANGELOG.md b/vhost-device-console/CHANGELOG.md index 6c84888..ca27dfd 100644 --- a/vhost-device-console/CHANGELOG.md +++ b/vhost-device-console/CHANGELOG.md @@ -3,6 +3,8 @@ ### Added +- [#811](https://github.com/rust-vmm/vhost-device/pull/811) Being able to specify max queue size + ### Changed ### Fixed diff --git a/vhost-device-console/README.md b/vhost-device-console/README.md index 5e5cd90..ddec153 100644 --- a/vhost-device-console/README.md +++ b/vhost-device-console/README.md @@ -47,6 +47,11 @@ vhost-device-console --socket-path= Note: The nested backend is selected by default and can be used only when socket_count equals 1. +.. option:: -q, --max-queue-size=SIZE + + The maximum size of virtqueues. It is optional, and the default value is + 128. The size must be greater than 0 and a power of 2. + ## Limitations This device is still work-in-progress (WIP). The current version has been tested diff --git a/vhost-device-console/src/backend.rs b/vhost-device-console/src/backend.rs index 5721242..cc5709e 100644 --- a/vhost-device-console/src/backend.rs +++ b/vhost-device-console/src/backend.rs @@ -50,6 +50,7 @@ pub struct VuConsoleConfig { pub backend: BackendType, pub tcp_port: String, pub socket_count: u32, + pub max_queue_size: usize, } impl VuConsoleConfig { @@ -86,12 +87,18 @@ impl VuConsoleConfig { /// This is the public API through which an external program starts the /// vhost-device-console backend server. -pub fn start_backend_server(socket: PathBuf, tcp_addr: String, backend: BackendType) -> Result<()> { +pub fn start_backend_server( + socket: PathBuf, + tcp_addr: String, + backend: BackendType, + max_queue_size: usize, +) -> Result<()> { loop { let controller = ConsoleController::new(backend); let arc_controller = Arc::new(RwLock::new(controller)); let vu_console_backend = Arc::new(RwLock::new( - VhostUserConsoleBackend::new(arc_controller).map_err(Error::CouldNotCreateBackend)?, + VhostUserConsoleBackend::new(max_queue_size, arc_controller) + .map_err(Error::CouldNotCreateBackend)?, )); vu_console_backend @@ -127,6 +134,7 @@ pub fn start_backend(config: VuConsoleConfig) -> Result<()> { let (senders, receiver) = std::sync::mpsc::channel(); let tcp_addrs = config.generate_tcp_addrs(); let backend = config.backend; + let max_queue_size = config.max_queue_size; for (thread_id, (socket, tcp_addr)) in config .generate_socket_paths() @@ -143,7 +151,7 @@ pub fn start_backend(config: VuConsoleConfig) -> Result<()> { .name(name.clone()) .spawn(move || { let result = std::panic::catch_unwind(move || { - start_backend_server(socket, tcp_addr.to_string(), backend) + start_backend_server(socket, tcp_addr.to_string(), backend, max_queue_size) }); // Notify the main thread that we are done. @@ -173,7 +181,7 @@ mod tests { use assert_matches::assert_matches; use super::*; - use crate::ConsoleArgs; + use crate::{ConsoleArgs, DEFAULT_QUEUE_SIZE}; #[test] fn test_console_valid_configuration_nested() { @@ -182,6 +190,7 @@ mod tests { backend: BackendType::Nested, tcp_port: String::from("12345"), socket_count: 1, + max_queue_size: DEFAULT_QUEUE_SIZE, }; VuConsoleConfig::try_from(args).unwrap(); @@ -194,6 +203,7 @@ mod tests { backend: BackendType::Nested, tcp_port: String::from("12345"), socket_count: 0, + max_queue_size: DEFAULT_QUEUE_SIZE, }; assert_matches!( @@ -209,6 +219,7 @@ mod tests { backend: BackendType::Nested, tcp_port: String::from("12345"), socket_count: 2, + max_queue_size: DEFAULT_QUEUE_SIZE, }; assert_matches!( @@ -224,6 +235,7 @@ mod tests { backend: BackendType::Network, tcp_port: String::from("12345"), socket_count: 1, + max_queue_size: DEFAULT_QUEUE_SIZE, }; VuConsoleConfig::try_from(args).unwrap(); @@ -236,6 +248,7 @@ mod tests { backend: BackendType::Network, tcp_port: String::from("12345"), socket_count: 2, + max_queue_size: DEFAULT_QUEUE_SIZE, }; VuConsoleConfig::try_from(args).unwrap(); @@ -246,13 +259,14 @@ mod tests { let tcp_addrs = config.generate_tcp_addrs(); let backend = config.backend; + let max_queue_size = config.max_queue_size; for (socket, tcp_addr) in config .generate_socket_paths() .into_iter() .zip(tcp_addrs.iter()) { - start_backend_server(socket, tcp_addr.to_string(), backend)?; + start_backend_server(socket, tcp_addr.to_string(), backend, max_queue_size)?; } Ok(()) } @@ -264,6 +278,7 @@ mod tests { backend: BackendType::Network, tcp_port: String::from("12345"), socket_count: 1, + max_queue_size: DEFAULT_QUEUE_SIZE, }; assert!(test_backend_start_and_stop(args).is_err()); @@ -276,6 +291,7 @@ mod tests { backend: BackendType::Network, tcp_port: String::from("12346"), socket_count: 1, + max_queue_size: DEFAULT_QUEUE_SIZE, }; assert!(start_backend(config).is_err()); diff --git a/vhost-device-console/src/main.rs b/vhost-device-console/src/main.rs index cd77a74..508e6ff 100644 --- a/vhost-device-console/src/main.rs +++ b/vhost-device-console/src/main.rs @@ -44,6 +44,8 @@ use crate::console::BackendType; pub type Result = std::result::Result; use crate::backend::{start_backend, Error, VuConsoleConfig}; +const DEFAULT_QUEUE_SIZE: usize = 128; + #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] struct ConsoleArgs { @@ -65,6 +67,10 @@ struct ConsoleArgs { /// `tcp_port + 1`, ... , `tcp_port + (N - 1)`. #[clap(short = 'p', long, value_name = "PORT", default_value = "12345")] tcp_port: String, + + /// Specify the maximum size of virtqueue, the default is 128. + #[clap(short = 'q', long, default_value_t = DEFAULT_QUEUE_SIZE)] + max_queue_size: usize, } impl TryFrom for VuConsoleConfig { @@ -84,6 +90,7 @@ impl TryFrom for VuConsoleConfig { backend, tcp_port, socket_count, + max_queue_size, } = args; Ok(Self { @@ -91,6 +98,7 @@ impl TryFrom for VuConsoleConfig { backend, tcp_port, socket_count, + max_queue_size, }) } } diff --git a/vhost-device-console/src/vhu_console.rs b/vhost-device-console/src/vhu_console.rs index ad97519..e6e532f 100644 --- a/vhost-device-console/src/vhu_console.rs +++ b/vhost-device-console/src/vhu_console.rs @@ -124,6 +124,7 @@ impl ReadWrite for T {} unsafe impl ByteValued for VirtioConsoleControl {} pub struct VhostUserConsoleBackend { + max_queue_size: usize, controller: Arc>, acked_features: u64, event_idx: bool, @@ -147,11 +148,11 @@ type ConsoleDescriptorChain = DescriptorChain>) -> Result { + pub fn new(max_queue_size: usize, controller: Arc>) -> Result { Ok(Self { + max_queue_size, controller, event_idx: false, rx_ctrl_fifo: Queue::new(), @@ -698,7 +699,7 @@ impl VhostUserBackendMut for VhostUserConsoleBackend { } fn max_queue_size(&self) -> usize { - Self::QUEUE_SIZE + self.max_queue_size } fn features(&self) -> u64 { @@ -863,12 +864,14 @@ mod tests { use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryMmap}; use super::*; + use crate::DEFAULT_QUEUE_SIZE; #[test] fn test_vhost_user_console_backend_creation() { let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested))); - let vhost_user_console_backend = VhostUserConsoleBackend::new(console_controller) - .expect("Failed create vhuconsole backend"); + let vhost_user_console_backend = + VhostUserConsoleBackend::new(DEFAULT_QUEUE_SIZE, console_controller) + .expect("Failed create vhuconsole backend"); assert_eq!(vhost_user_console_backend.acked_features, 0); assert!(!vhost_user_console_backend.event_idx); @@ -879,8 +882,9 @@ mod tests { #[test] fn test_virtio_console_empty_handle_request() { let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested))); - let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller) - .expect("Failed create vhuconsole backend"); + let mut vu_console_backend = + VhostUserConsoleBackend::new(DEFAULT_QUEUE_SIZE, console_controller) + .expect("Failed create vhuconsole backend"); let mem = GuestMemoryAtomic::new( GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(), @@ -926,8 +930,9 @@ mod tests { #[test] fn test_virtio_console_empty_requests() { let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested))); - let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller) - .expect("Failed create vhuconsole backend"); + let mut vu_console_backend = + VhostUserConsoleBackend::new(DEFAULT_QUEUE_SIZE, console_controller) + .expect("Failed create vhuconsole backend"); let mem = GuestMemoryAtomic::new( GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(), @@ -985,8 +990,9 @@ mod tests { #[test] fn test_virtio_console_ctrl_rx_request() { let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested))); - let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller) - .expect("Failed create vhuconsole backend"); + let mut vu_console_backend = + VhostUserConsoleBackend::new(DEFAULT_QUEUE_SIZE, console_controller) + .expect("Failed create vhuconsole backend"); let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); @@ -1059,8 +1065,9 @@ mod tests { #[test] fn test_virtio_console_ctrl_tx_request() { let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested))); - let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller) - .expect("Failed create vhuconsole backend"); + let mut vu_console_backend = + VhostUserConsoleBackend::new(DEFAULT_QUEUE_SIZE, console_controller) + .expect("Failed create vhuconsole backend"); let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); @@ -1109,8 +1116,9 @@ mod tests { #[test] fn test_virtio_console_handle_control_msg() { let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested))); - let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller) - .expect("Failed create vhuconsole backend"); + let mut vu_console_backend = + VhostUserConsoleBackend::new(DEFAULT_QUEUE_SIZE, console_controller) + .expect("Failed create vhuconsole backend"); let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); let mem_1 = GuestMemoryAtomic::new(mem.clone()); @@ -1162,8 +1170,9 @@ mod tests { #[test] fn test_virtio_console_tx_request() { let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested))); - let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller) - .expect("Failed create vhuconsole backend"); + let mut vu_console_backend = + VhostUserConsoleBackend::new(DEFAULT_QUEUE_SIZE, console_controller) + .expect("Failed create vhuconsole backend"); let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); @@ -1217,8 +1226,9 @@ mod tests { fn test_virtio_console_tx_request_network() { let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Network))); - let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller) - .expect("Failed create vhuconsole backend"); + let mut vu_console_backend = + VhostUserConsoleBackend::new(DEFAULT_QUEUE_SIZE, console_controller) + .expect("Failed create vhuconsole backend"); let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); let desc_chain = build_desc_chain(&mem, 1, vec![0], 0x200); @@ -1258,8 +1268,9 @@ mod tests { #[test] fn test_virtio_console_rx_request() { let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested))); - let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller) - .expect("Failed create vhuconsole backend"); + let mut vu_console_backend = + VhostUserConsoleBackend::new(DEFAULT_QUEUE_SIZE, console_controller) + .expect("Failed create vhuconsole backend"); let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); @@ -1331,8 +1342,9 @@ mod tests { #[test] fn test_virtio_console_start_nested_console_thread() { let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested))); - let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller) - .expect("Failed create vhuconsole backend"); + let mut vu_console_backend = + VhostUserConsoleBackend::new(DEFAULT_QUEUE_SIZE, console_controller) + .expect("Failed create vhuconsole backend"); let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); let mem = GuestMemoryAtomic::new(mem); @@ -1360,8 +1372,9 @@ mod tests { fn test_virtio_console_tcp_console_read_func() { let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Network))); - let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller) - .expect("Failed create vhuconsole backend"); + let mut vu_console_backend = + VhostUserConsoleBackend::new(DEFAULT_QUEUE_SIZE, console_controller) + .expect("Failed create vhuconsole backend"); let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); let mem = GuestMemoryAtomic::new(mem); @@ -1389,8 +1402,9 @@ mod tests { fn test_virtio_console_tcp_console_write_func() { let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Network))); - let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller) - .expect("Failed create vhuconsole backend"); + let mut vu_console_backend = + VhostUserConsoleBackend::new(DEFAULT_QUEUE_SIZE, console_controller) + .expect("Failed create vhuconsole backend"); let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); let mem = GuestMemoryAtomic::new(mem);