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 <rbradford@meta.com>
This commit is contained in:
Rob Bradford
2026-07-07 09:14:23 +01:00
committed by Wei Liu
parent ba3cfd7d9d
commit ad3dbcd130
7 changed files with 12 additions and 6 deletions

1
Cargo.lock generated
View File

@@ -2707,6 +2707,7 @@ dependencies = [
"serde", "serde",
"serde_with", "serde_with",
"serial_buffer", "serial_buffer",
"smallvec",
"thiserror", "thiserror",
"vhost", "vhost",
"virtio-bindings", "virtio-bindings",

View File

@@ -105,6 +105,7 @@ rustls = { version = "0.23.40", default-features = false, features = [
sha2 = "0.11.0" sha2 = "0.11.0"
signal-hook = "0.4.4" signal-hook = "0.4.4"
signal-hook-registry = "1.4.8" signal-hook-registry = "1.4.8"
smallvec = "1.15.1"
thiserror = "2.0.18" thiserror = "2.0.18"
uuid = { version = "1.23.2" } uuid = { version = "1.23.2" }
wait-timeout = "0.2.1" wait-timeout = "0.2.1"

View File

@@ -20,7 +20,7 @@ libc = { workspace = true }
log = { workspace = true } log = { workspace = true }
remain = "0.2.15" remain = "0.2.15"
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
smallvec = "1.15.1" smallvec = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
uuid = { workspace = true, features = ["v4"] } uuid = { workspace = true, features = ["v4"] }
virtio-bindings = { workspace = true } virtio-bindings = { workspace = true }

View File

@@ -30,6 +30,7 @@ serde_with = { workspace = true, default-features = false, features = [
"macros", "macros",
] } ] }
serial_buffer = { path = "../serial_buffer" } serial_buffer = { path = "../serial_buffer" }
smallvec = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
vhost = { workspace = true, features = [ vhost = { workspace = true, features = [
"vhost-kern", "vhost-kern",

View File

@@ -25,6 +25,7 @@ use event_monitor::event;
use log::{error, info, warn}; use log::{error, info, warn};
use seccompiler::SeccompAction; use seccompiler::SeccompAction;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use thiserror::Error; use thiserror::Error;
use virtio_queue::{Queue, QueueT}; use virtio_queue::{Queue, QueueT};
use vm_allocator::page_size::{align_page_size_down, get_page_size}; use vm_allocator::page_size::{align_page_size_down, get_page_size};
@@ -276,7 +277,7 @@ impl BalloonEpollHandler {
{ {
let data_chunk_size = size_of::<u32>(); let data_chunk_size = size_of::<u32>();
let results: Vec<_> = desc_chain let results: SmallVec<[_; 4]> = desc_chain
.checked_iter(self.access_platform.as_deref()) .checked_iter(self.access_platform.as_deref())
.collect(); .collect();
for result in results { for result in results {
@@ -366,7 +367,7 @@ impl BalloonEpollHandler {
self.queues[queue_index].pop_descriptor_chain(self.mem.memory()) self.queues[queue_index].pop_descriptor_chain(self.mem.memory())
{ {
let mut descs_len = 0; let mut descs_len = 0;
let results: Vec<_> = desc_chain let results: SmallVec<[_; 4]> = desc_chain
.checked_iter(self.access_platform.as_deref()) .checked_iter(self.access_platform.as_deref())
.collect(); .collect();
for result in results { for result in results {

View File

@@ -16,6 +16,7 @@ use log::{error, info, warn};
use seccompiler::SeccompAction; use seccompiler::SeccompAction;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serial_buffer::SerialBuffer; use serial_buffer::SerialBuffer;
use smallvec::SmallVec;
use thiserror::Error; use thiserror::Error;
use virtio_queue::{Queue, QueueT}; use virtio_queue::{Queue, QueueT};
use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic}; use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic};
@@ -205,7 +206,7 @@ impl ConsoleEpollHandler {
// Validate all descriptors upfront so we never partially fill // Validate all descriptors upfront so we never partially fill
// buffers for a chain that turns out to be invalid. // 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()) .checked_iter(self.access_platform.as_deref())
.collect::<Result<_, _>>() .collect::<Result<_, _>>()
.unwrap_or_default(); .unwrap_or_default();
@@ -253,7 +254,7 @@ impl ConsoleEpollHandler {
let mut used_descs = false; let mut used_descs = false;
while let Some(mut desc_chain) = trans_queue.pop_descriptor_chain(self.mem.memory()) { 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()) .checked_iter(self.access_platform.as_deref())
.collect(); .collect();
for result in results { for result in results {

View File

@@ -15,6 +15,7 @@ use event_monitor::event;
use log::{error, info, warn}; use log::{error, info, warn};
use seccompiler::SeccompAction; use seccompiler::SeccompAction;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use thiserror::Error; use thiserror::Error;
use virtio_queue::{Queue, QueueT}; use virtio_queue::{Queue, QueueT};
use vm_memory::{Bytes, GuestAddressSpace, GuestMemoryAtomic}; use vm_memory::{Bytes, GuestAddressSpace, GuestMemoryAtomic};
@@ -66,7 +67,7 @@ impl RngEpollHandler {
// Validate the entire descriptor chain upfront so we never // Validate the entire descriptor chain upfront so we never
// partially fill buffers for a chain that turns out to be // partially fill buffers for a chain that turns out to be
// invalid. // invalid.
let descs: Vec<_> = desc_chain let descs: SmallVec<[_; 4]> = desc_chain
.checked_iter(self.access_platform.as_deref()) .checked_iter(self.access_platform.as_deref())
.collect::<Result<_, _>>() .collect::<Result<_, _>>()
.unwrap_or_default(); .unwrap_or_default();