From ad3dbcd1307295e0c86f50ccf139372053a041b7 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Tue, 7 Jul 2026 09:14:23 +0100 Subject: [PATCH] virtio-devices: Use SmallVec for descriptor chains Rather than instantiating a vector for parsing the descriptor chain in advance instead use a SmallVec bounded by the expected length of the descriptor chain. This removes vector allocations from those paths. As smallvec was already a block dependency move it to a workspace dependency and use it from there. Fixes: #5079 Signed-off-by: Rob Bradford --- Cargo.lock | 1 + Cargo.toml | 1 + block/Cargo.toml | 2 +- virtio-devices/Cargo.toml | 1 + virtio-devices/src/balloon.rs | 5 +++-- virtio-devices/src/console.rs | 5 +++-- virtio-devices/src/rng.rs | 3 ++- 7 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ea14aa60d..ea3157441 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2707,6 +2707,7 @@ dependencies = [ "serde", "serde_with", "serial_buffer", + "smallvec", "thiserror", "vhost", "virtio-bindings", diff --git a/Cargo.toml b/Cargo.toml index 4683f618d..38bb98818 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,6 +105,7 @@ rustls = { version = "0.23.40", default-features = false, features = [ sha2 = "0.11.0" signal-hook = "0.4.4" signal-hook-registry = "1.4.8" +smallvec = "1.15.1" thiserror = "2.0.18" uuid = { version = "1.23.2" } wait-timeout = "0.2.1" diff --git a/block/Cargo.toml b/block/Cargo.toml index 974e1afc8..690d5f79e 100644 --- a/block/Cargo.toml +++ b/block/Cargo.toml @@ -20,7 +20,7 @@ libc = { workspace = true } log = { workspace = true } remain = "0.2.15" serde = { workspace = true, features = ["derive"] } -smallvec = "1.15.1" +smallvec = { workspace = true } thiserror = { workspace = true } uuid = { workspace = true, features = ["v4"] } virtio-bindings = { workspace = true } diff --git a/virtio-devices/Cargo.toml b/virtio-devices/Cargo.toml index d2658eeec..2ef499f8d 100644 --- a/virtio-devices/Cargo.toml +++ b/virtio-devices/Cargo.toml @@ -30,6 +30,7 @@ serde_with = { workspace = true, default-features = false, features = [ "macros", ] } serial_buffer = { path = "../serial_buffer" } +smallvec = { workspace = true } thiserror = { workspace = true } vhost = { workspace = true, features = [ "vhost-kern", diff --git a/virtio-devices/src/balloon.rs b/virtio-devices/src/balloon.rs index 1a1e2198e..eb90cd36d 100644 --- a/virtio-devices/src/balloon.rs +++ b/virtio-devices/src/balloon.rs @@ -25,6 +25,7 @@ use event_monitor::event; use log::{error, info, warn}; use seccompiler::SeccompAction; use serde::{Deserialize, Serialize}; +use smallvec::SmallVec; use thiserror::Error; use virtio_queue::{Queue, QueueT}; use vm_allocator::page_size::{align_page_size_down, get_page_size}; @@ -276,7 +277,7 @@ impl BalloonEpollHandler { { let data_chunk_size = size_of::(); - let results: Vec<_> = desc_chain + let results: SmallVec<[_; 4]> = desc_chain .checked_iter(self.access_platform.as_deref()) .collect(); for result in results { @@ -366,7 +367,7 @@ impl BalloonEpollHandler { self.queues[queue_index].pop_descriptor_chain(self.mem.memory()) { let mut descs_len = 0; - let results: Vec<_> = desc_chain + let results: SmallVec<[_; 4]> = desc_chain .checked_iter(self.access_platform.as_deref()) .collect(); for result in results { diff --git a/virtio-devices/src/console.rs b/virtio-devices/src/console.rs index e05db9e93..608cdbaf8 100644 --- a/virtio-devices/src/console.rs +++ b/virtio-devices/src/console.rs @@ -16,6 +16,7 @@ use log::{error, info, warn}; use seccompiler::SeccompAction; use serde::{Deserialize, Serialize}; use serial_buffer::SerialBuffer; +use smallvec::SmallVec; use thiserror::Error; use virtio_queue::{Queue, QueueT}; use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic}; @@ -205,7 +206,7 @@ impl ConsoleEpollHandler { // Validate all descriptors upfront so we never partially fill // buffers for a chain that turns out to be invalid. - let descs: Vec<_> = desc_chain + let descs: SmallVec<[_; 4]> = desc_chain .checked_iter(self.access_platform.as_deref()) .collect::>() .unwrap_or_default(); @@ -253,7 +254,7 @@ impl ConsoleEpollHandler { let mut used_descs = false; while let Some(mut desc_chain) = trans_queue.pop_descriptor_chain(self.mem.memory()) { - let results: Vec<_> = desc_chain + let results: SmallVec<[_; 4]> = desc_chain .checked_iter(self.access_platform.as_deref()) .collect(); for result in results { diff --git a/virtio-devices/src/rng.rs b/virtio-devices/src/rng.rs index eae688ef0..b76557ed7 100644 --- a/virtio-devices/src/rng.rs +++ b/virtio-devices/src/rng.rs @@ -15,6 +15,7 @@ use event_monitor::event; use log::{error, info, warn}; use seccompiler::SeccompAction; use serde::{Deserialize, Serialize}; +use smallvec::SmallVec; use thiserror::Error; use virtio_queue::{Queue, QueueT}; use vm_memory::{Bytes, GuestAddressSpace, GuestMemoryAtomic}; @@ -66,7 +67,7 @@ impl RngEpollHandler { // Validate the entire descriptor chain upfront so we never // partially fill buffers for a chain that turns out to be // invalid. - let descs: Vec<_> = desc_chain + let descs: SmallVec<[_; 4]> = desc_chain .checked_iter(self.access_platform.as_deref()) .collect::>() .unwrap_or_default();