vmm: config: Reject rate limiting with vhost_user

Rate limiting is implemented in the virtio device layer and does not
apply to vhost-user devices which delegate I/O handling to an external
process.

Add validation to reject configurations where vhost_user is enabled
along with rate limiting options (bw_size, ops_size, or
rate_limit_group) for both disk and network devices.

This prevents users from mistakenly configuring rate limiting that would
be silently ignored when using vhost-user backends.

Signed-off-by: Rob Bradford <rbradford@meta.com>
This commit is contained in:
Rob Bradford
2026-05-01 17:44:58 +01:00
committed by Bo Chen
parent 7fbc5a1354
commit f09ab0898d

View File

@@ -348,6 +348,9 @@ pub enum ValidationError {
/// Invalid rate-limiter group
#[error("Invalid rate-limiter group")]
InvalidRateLimiterGroup,
/// Rate limiting is not supported with vhost-user
#[error("Rate limiting is not supported with vhost-user")]
VhostUserRateLimiterNotSupported,
/// The specified I/O port was invalid. It should be provided in hex, such as `0xe9`.
#[cfg(target_arch = "x86_64")]
#[error("The IO port was not properly provided in hex or a `0x` prefix is missing: {0}")]
@@ -1480,6 +1483,14 @@ impl DiskConfig {
return Err(ValidationError::IommuNotSupported);
}
if self.vhost_user && self.rate_limiter_config.is_some() {
return Err(ValidationError::VhostUserRateLimiterNotSupported);
}
if self.vhost_user && self.rate_limit_group.is_some() {
return Err(ValidationError::VhostUserRateLimiterNotSupported);
}
if self.rate_limiter_config.is_some() && self.rate_limit_group.is_some() {
return Err(ValidationError::InvalidRateLimiterGroup);
}
@@ -1709,6 +1720,10 @@ impl NetConfig {
return Err(ValidationError::IommuNotSupported);
}
if self.vhost_user && self.rate_limiter_config.is_some() {
return Err(ValidationError::VhostUserRateLimiterNotSupported);
}
if let Some(mtu) = self.mtu
&& mtu < virtio_devices::net::MIN_MTU
{
@@ -2913,6 +2928,11 @@ impl VmConfig {
if disk.vhost_user && disk.vhost_socket.is_none() {
return Err(ValidationError::VhostUserMissingSocket);
}
if disk.vhost_user
&& (disk.rate_limiter_config.is_some() || disk.rate_limit_group.is_some())
{
return Err(ValidationError::VhostUserRateLimiterNotSupported);
}
if let Some(rate_limit_group) = &disk.rate_limit_group {
if let Some(rate_limit_groups) = &self.rate_limit_groups {
if !rate_limit_groups
@@ -2938,6 +2958,9 @@ impl VmConfig {
if net.vhost_user && !self.backed_by_shared_memory() {
return Err(ValidationError::VhostUserRequiresSharedMemory);
}
if net.vhost_user && net.rate_limiter_config.is_some() {
return Err(ValidationError::VhostUserRateLimiterNotSupported);
}
net.validate(self)?;
self.iommu |= net.pci_common.iommu;
@@ -5162,6 +5185,50 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
still_valid_config.memory.shared = true;
still_valid_config.validate().unwrap();
// Test vhost_user with rate limiting for disk
let mut invalid_config = valid_config.clone();
invalid_config.memory.shared = true;
invalid_config.disks = Some(vec![DiskConfig {
path: None,
vhost_user: true,
vhost_socket: Some("/path/to/sock".to_owned()),
rate_limiter_config: Some(RateLimiterConfig::default()),
..disk_fixture()
}]);
assert_eq!(
invalid_config.validate(),
Err(ValidationError::VhostUserRateLimiterNotSupported)
);
// Test vhost_user with rate_limit_group for disk
let mut invalid_config = valid_config.clone();
invalid_config.memory.shared = true;
invalid_config.disks = Some(vec![DiskConfig {
path: None,
vhost_user: true,
vhost_socket: Some("/path/to/sock".to_owned()),
rate_limit_group: Some("group0".to_string()),
..disk_fixture()
}]);
assert_eq!(
invalid_config.validate(),
Err(ValidationError::VhostUserRateLimiterNotSupported)
);
// Test vhost_user with rate limiting for net
let mut invalid_config = valid_config.clone();
invalid_config.memory.shared = true;
invalid_config.net = Some(vec![NetConfig {
vhost_user: true,
vhost_socket: Some("/path/to/sock".to_owned()),
rate_limiter_config: Some(RateLimiterConfig::default()),
..net_fixture()
}]);
assert_eq!(
invalid_config.validate(),
Err(ValidationError::VhostUserRateLimiterNotSupported)
);
let mut invalid_config = valid_config.clone();
invalid_config.net = Some(vec![NetConfig {
fds: Some(vec![0]),