mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-09-24 02:07:31 +00:00
runtime-rs: add the block devices io limit support
Given that Rust-based VMMs like cloud-hypervisor, Firecracker, and Dragonball naturally offer user-level block I/O rate limiting, I/O throttling has been implemented to leverage this capability for these VMMs. This PR specifically introduces support for cloud-hypervisor. Signed-off-by: Fupan Li <fupan.lfp@antgroup.com>
This commit is contained in:
committed by
Fabiano Fidêncio
parent
ac74ef4505
commit
73e31ea19a
@@ -173,6 +173,19 @@ pub struct BlockDeviceInfo {
|
||||
/// The default if not set is empty (all annotations rejected.)
|
||||
#[serde(default)]
|
||||
pub valid_vhost_user_store_paths: Vec<String>,
|
||||
|
||||
/// controls disk I/O bandwidth (size in bits/sec)
|
||||
#[serde(default)]
|
||||
pub disk_rate_limiter_bw_max_rate: u64,
|
||||
/// increases the initial max rate
|
||||
#[serde(default)]
|
||||
pub disk_rate_limiter_bw_one_time_burst: Option<u64>,
|
||||
/// controls disk I/O bandwidth (size in ops/sec)
|
||||
#[serde(default)]
|
||||
pub disk_rate_limiter_ops_max_rate: u64,
|
||||
/// increases the initial max rate
|
||||
#[serde(default)]
|
||||
pub disk_rate_limiter_ops_one_time_burst: Option<u64>,
|
||||
}
|
||||
|
||||
impl BlockDeviceInfo {
|
||||
|
@@ -178,6 +178,36 @@ block_device_driver = "virtio-blk-pci"
|
||||
# Default false
|
||||
#block_device_cache_direct = true
|
||||
|
||||
# Bandwidth rate limiter options
|
||||
#
|
||||
# disk_rate_limiter_bw_max_rate controls disk I/O bandwidth (size in bits/sec
|
||||
# for SB/VM).
|
||||
# The same value is used for inbound and outbound bandwidth.
|
||||
# Default 0-sized value means unlimited rate.
|
||||
#disk_rate_limiter_bw_max_rate = 0
|
||||
#
|
||||
# disk_rate_limiter_bw_one_time_burst increases the initial max rate and this
|
||||
# initial extra credit does *NOT* affect the overall limit and can be used for
|
||||
# an *initial* burst of data.
|
||||
# This is *optional* and only takes effect if disk_rate_limiter_bw_max_rate is
|
||||
# set to a non zero value.
|
||||
#disk_rate_limiter_bw_one_time_burst = 0
|
||||
#
|
||||
# Operation rate limiter options
|
||||
#
|
||||
# disk_rate_limiter_ops_max_rate controls disk I/O bandwidth (size in ops/sec
|
||||
# for SB/VM).
|
||||
# The same value is used for inbound and outbound bandwidth.
|
||||
# Default 0-sized value means unlimited rate.
|
||||
#disk_rate_limiter_ops_max_rate = 0
|
||||
#
|
||||
# disk_rate_limiter_ops_one_time_burst increases the initial max rate and this
|
||||
# initial extra credit does *NOT* affect the overall limit and can be used for
|
||||
# an *initial* burst of data.
|
||||
# This is *optional* and only takes effect if disk_rate_limiter_bw_max_rate is
|
||||
# set to a non zero value.
|
||||
#disk_rate_limiter_ops_one_time_burst = 0
|
||||
|
||||
# Enable pre allocation of VM RAM, default false
|
||||
# Enabling this will result in lower container density
|
||||
# as all of the memory will be allocated and locked
|
||||
|
@@ -11,7 +11,7 @@ pub mod convert;
|
||||
pub mod net_util;
|
||||
mod virtio_devices;
|
||||
|
||||
use crate::virtio_devices::RateLimiterConfig;
|
||||
pub use crate::virtio_devices::RateLimiterConfig;
|
||||
use kata_sys_util::protection::GuestProtection;
|
||||
use kata_types::config::hypervisor::Hypervisor as HypervisorConfig;
|
||||
pub use net_util::MacAddr;
|
||||
|
@@ -4,6 +4,11 @@
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// The DEFAULT_RATE_LIMITER_REFILL_TIME is used for calculating the rate at
|
||||
// which a TokenBucket is replinished, in cases where a RateLimiter is
|
||||
// applied to either network or disk I/O.
|
||||
pub(crate) const DEFAULT_RATE_LIMITER_REFILL_TIME: u64 = 1000;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub struct TokenBucketConfig {
|
||||
pub size: u64,
|
||||
@@ -17,3 +22,131 @@ pub struct RateLimiterConfig {
|
||||
pub bandwidth: Option<TokenBucketConfig>,
|
||||
pub ops: Option<TokenBucketConfig>,
|
||||
}
|
||||
|
||||
impl RateLimiterConfig {
|
||||
/// Helper function: Creates a `TokenBucketConfig` based on the provided rate and burst.
|
||||
/// Returns `None` if the `rate` is 0.
|
||||
fn create_token_bucket_config(
|
||||
rate: u64,
|
||||
one_time_burst: Option<u64>,
|
||||
) -> Option<TokenBucketConfig> {
|
||||
if rate > 0 {
|
||||
Some(TokenBucketConfig {
|
||||
size: rate,
|
||||
one_time_burst,
|
||||
refill_time: DEFAULT_RATE_LIMITER_REFILL_TIME,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `RateLimiterConfig` instance.
|
||||
///
|
||||
/// If both `band_rate` and `ops_rate` are 0 (indicating no rate limiting configured),
|
||||
/// it returns `None`. Otherwise, it returns `Some(RateLimiterConfig)` containing
|
||||
/// the configured options.
|
||||
pub fn new(
|
||||
band_rate: u64,
|
||||
ops_rate: u64,
|
||||
band_onetime_burst: Option<u64>,
|
||||
ops_onetime_burst: Option<u64>,
|
||||
) -> Option<RateLimiterConfig> {
|
||||
// Use the helper function to create `TokenBucketConfig` for bandwidth and ops
|
||||
let bandwidth = Self::create_token_bucket_config(band_rate, band_onetime_burst);
|
||||
let ops = Self::create_token_bucket_config(ops_rate, ops_onetime_burst);
|
||||
|
||||
// Use pattern matching to concisely handle the final `Option<RateLimiterConfig>` return.
|
||||
// If both bandwidth and ops are `None`, the entire config is `None`.
|
||||
// Otherwise, return `Some` with the actual configured options.
|
||||
match (bandwidth, ops) {
|
||||
(None, None) => None,
|
||||
(b, o) => Some(RateLimiterConfig {
|
||||
bandwidth: b,
|
||||
ops: o,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unit tests
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_new_all_set() {
|
||||
let config = RateLimiterConfig::new(100, 50, Some(10), Some(5)).unwrap();
|
||||
assert_eq!(
|
||||
config.bandwidth,
|
||||
Some(TokenBucketConfig {
|
||||
size: 100,
|
||||
one_time_burst: Some(10),
|
||||
refill_time: DEFAULT_RATE_LIMITER_REFILL_TIME
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
config.ops,
|
||||
Some(TokenBucketConfig {
|
||||
size: 50,
|
||||
one_time_burst: Some(5),
|
||||
refill_time: DEFAULT_RATE_LIMITER_REFILL_TIME
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_bandwidth_only() {
|
||||
let config = RateLimiterConfig::new(100, 0, Some(10), None).unwrap();
|
||||
assert_eq!(
|
||||
config.bandwidth,
|
||||
Some(TokenBucketConfig {
|
||||
size: 100,
|
||||
one_time_burst: Some(10),
|
||||
refill_time: DEFAULT_RATE_LIMITER_REFILL_TIME
|
||||
})
|
||||
);
|
||||
assert_eq!(config.ops, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_ops_only() {
|
||||
let config = RateLimiterConfig::new(0, 50, None, Some(5)).unwrap();
|
||||
assert_eq!(config.bandwidth, None);
|
||||
assert_eq!(
|
||||
config.ops,
|
||||
Some(TokenBucketConfig {
|
||||
size: 50,
|
||||
one_time_burst: Some(5),
|
||||
refill_time: DEFAULT_RATE_LIMITER_REFILL_TIME
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_no_burst() {
|
||||
let config = RateLimiterConfig::new(100, 50, None, None).unwrap();
|
||||
assert_eq!(
|
||||
config.bandwidth,
|
||||
Some(TokenBucketConfig {
|
||||
size: 100,
|
||||
one_time_burst: None,
|
||||
refill_time: DEFAULT_RATE_LIMITER_REFILL_TIME
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
config.ops,
|
||||
Some(TokenBucketConfig {
|
||||
size: 50,
|
||||
one_time_burst: None,
|
||||
refill_time: DEFAULT_RATE_LIMITER_REFILL_TIME
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_none_set() {
|
||||
let config = RateLimiterConfig::new(0, 0, None, None);
|
||||
assert_eq!(config, None);
|
||||
}
|
||||
}
|
||||
|
@@ -24,7 +24,9 @@ use ch_config::ch_api::{
|
||||
};
|
||||
use ch_config::convert::{DEFAULT_DISK_QUEUES, DEFAULT_DISK_QUEUE_SIZE, DEFAULT_NUM_PCI_SEGMENTS};
|
||||
use ch_config::DiskConfig;
|
||||
use ch_config::{net_util::MacAddr, DeviceConfig, FsConfig, NetConfig, VsockConfig};
|
||||
use ch_config::{
|
||||
net_util::MacAddr, DeviceConfig, FsConfig, NetConfig, RateLimiterConfig, VsockConfig,
|
||||
};
|
||||
use safe_path::scoped_join;
|
||||
use std::convert::TryFrom;
|
||||
use std::path::PathBuf;
|
||||
@@ -322,6 +324,18 @@ impl CloudHypervisorInner {
|
||||
.is_direct
|
||||
.unwrap_or(self.config.blockdev_info.block_device_cache_direct);
|
||||
|
||||
let block_rate_limit = RateLimiterConfig::new(
|
||||
self.config.blockdev_info.disk_rate_limiter_bw_max_rate,
|
||||
self.config.blockdev_info.disk_rate_limiter_ops_max_rate,
|
||||
self.config
|
||||
.blockdev_info
|
||||
.disk_rate_limiter_bw_one_time_burst,
|
||||
self.config
|
||||
.blockdev_info
|
||||
.disk_rate_limiter_ops_one_time_burst,
|
||||
);
|
||||
disk_config.rate_limiter_config = block_rate_limit;
|
||||
|
||||
let response = cloud_hypervisor_vm_blockdev_add(
|
||||
socket.try_clone().context("failed to clone socket")?,
|
||||
disk_config,
|
||||
|
Reference in New Issue
Block a user