Skip to content

Commit 5ac9d97

Browse files
dhrgitalxiord
authored andcommitted
rate_limiter: removed custom deserialization logic
Removed the RateLimiter partial serialization logic, removing a de-facto dependency on the VMM / custom Firecracker logic. Also changed the VMM code that relied on the RateLimiter serialization. Signed-off-by: Dan Horobeanu <[email protected]>
1 parent 09c44ba commit 5ac9d97

File tree

7 files changed

+62
-148
lines changed

7 files changed

+62
-148
lines changed

api_server/src/request/net.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,14 @@ impl IntoParsedRequest for NetworkInterfaceUpdateConfig {
5555
#[cfg(test)]
5656
mod tests {
5757
extern crate net_util;
58-
extern crate rate_limiter;
58+
extern crate vmm;
5959

6060
use self::net_util::MacAddr;
6161
use super::*;
6262

6363
use serde_json;
6464

65-
use self::rate_limiter::RateLimiter;
65+
use self::vmm::vmm_config::RateLimiterConfig;
6666

6767
fn get_dummy_netif(
6868
iface_id: String,
@@ -117,8 +117,8 @@ mod tests {
117117
iface_id: String::from("foo"),
118118
host_dev_name: String::from("bar"),
119119
guest_mac: Some(MacAddr::parse_str("12:34:56:78:9A:BC").unwrap()),
120-
rx_rate_limiter: Some(RateLimiter::default()),
121-
tx_rate_limiter: Some(RateLimiter::default()),
120+
rx_rate_limiter: Some(RateLimiterConfig::default()),
121+
tx_rate_limiter: Some(RateLimiterConfig::default()),
122122
allow_mmds_requests: true,
123123
tap: None,
124124
};

rate_limiter/src/lib.rs

Lines changed: 9 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ extern crate serde_derive;
5454
#[macro_use]
5555
extern crate logger;
5656

57-
use serde::de::{self, Deserialize, Deserializer, MapAccess, Visitor};
5857
use std::os::unix::io::{AsRawFd, RawFd};
5958
use std::time::Duration;
6059
use std::{fmt, io};
@@ -290,67 +289,6 @@ impl fmt::Debug for RateLimiter {
290289
}
291290
}
292291

293-
impl<'de> Deserialize<'de> for RateLimiter {
294-
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
295-
where
296-
D: Deserializer<'de>,
297-
{
298-
#[derive(Deserialize)]
299-
#[allow(non_camel_case_types)]
300-
enum Field {
301-
bandwidth,
302-
ops,
303-
};
304-
305-
struct RateLimiterVisitor;
306-
307-
impl<'de> Visitor<'de> for RateLimiterVisitor {
308-
type Value = RateLimiter;
309-
310-
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
311-
formatter.write_str("struct RateLimiter")
312-
}
313-
314-
fn visit_map<V>(self, mut map: V) -> Result<RateLimiter, V::Error>
315-
where
316-
V: MapAccess<'de>,
317-
{
318-
let mut bandwidth: Option<TokenBucket> = None;
319-
let mut ops: Option<TokenBucket> = None;
320-
while let Some(key) = map.next_key()? {
321-
match key {
322-
Field::bandwidth => {
323-
if bandwidth.is_some() {
324-
return Err(de::Error::duplicate_field("bandwidth"));
325-
}
326-
bandwidth = Some(map.next_value()?);
327-
}
328-
Field::ops => {
329-
if ops.is_some() {
330-
return Err(de::Error::duplicate_field("ops"));
331-
}
332-
ops = Some(map.next_value()?);
333-
}
334-
}
335-
}
336-
let bandwidth = bandwidth.unwrap_or_default();
337-
let ops = ops.unwrap_or_default();
338-
RateLimiter::new(
339-
bandwidth.size,
340-
bandwidth.one_time_burst,
341-
bandwidth.refill_time,
342-
ops.size,
343-
ops.one_time_burst,
344-
ops.refill_time,
345-
)
346-
.map_err(de::Error::custom)
347-
}
348-
}
349-
const FIELDS: &'static [&'static str] = &["bandwidth", "ops"];
350-
deserializer.deserialize_struct("RateLimiter", FIELDS, RateLimiterVisitor)
351-
}
352-
}
353-
354292
impl RateLimiter {
355293
// description
356294
fn make_bucket(
@@ -499,9 +437,14 @@ impl RateLimiter {
499437
/// Updates the parameters of the token buckets associated with this RateLimiter.
500438
// TODO: Pls note that, right now, the buckets become full after being updated.
501439
pub fn update_buckets(&mut self, bytes: Option<TokenBucket>, ops: Option<TokenBucket>) {
502-
// TODO: We have to call make_bucket instead of directly assigning the bytes and/or ops
503-
// because the input buckets are likely build via deserialization, which currently does not
504-
// properly set up their internal state.
440+
// TODO: We should reconcile the create and update paths, such that they use the same data
441+
// format. Currently, the TokenBucket config data is used for create, but the live
442+
// TokenBucket objects are used for update.
443+
// We have to call make_bucket instead of directly assigning the bytes and/or ops
444+
// because the RateLimiter validates the TokenBucket config data (e.g. it nullifies
445+
// an unusable bucket with size 0). This is needed, because passing a 0-sized bucket is
446+
// the only method the user has to disable rate limiting. I.e. if the user passes `null`
447+
// as the token bucket config, the old config is left unchanged.
505448

506449
if let Some(b) = bytes {
507450
self.bandwidth = Self::make_bucket(b.size, b.one_time_burst, b.refill_time);
@@ -804,51 +747,9 @@ mod tests {
804747
//assert!(!l.consume(u64::max_value(), TokenType::Bytes));
805748
}
806749

807-
#[test]
808-
fn test_rate_limiter_deserialization() {
809-
let jstr = r#"{
810-
"bandwidth": { "size": 1000, "one_time_burst": 2000, "refill_time": 1000 },
811-
"ops": { "size": 10, "one_time_burst": 20, "refill_time": 1000 }
812-
}"#;
813-
814-
let x: RateLimiter = serde_json::from_str(jstr).expect("deserialization failed.");
815-
assert_eq!(x.bandwidth.as_ref().unwrap().size, 1000);
816-
assert_eq!(x.bandwidth.as_ref().unwrap().one_time_burst.unwrap(), 2000);
817-
assert_eq!(x.bandwidth.as_ref().unwrap().refill_time, 1000);
818-
assert_eq!(x.ops.as_ref().unwrap().size, 10);
819-
assert_eq!(x.ops.as_ref().unwrap().one_time_burst.unwrap(), 20);
820-
assert_eq!(x.ops.as_ref().unwrap().refill_time, 1000);
821-
822-
let jstr = r#"{
823-
"ops": { "size": 10, "one_time_burst": 20, "refill_time": 1000 }
824-
}"#;
825-
826-
let x: RateLimiter = serde_json::from_str(jstr).expect("deserialization failed.");
827-
assert!(x.bandwidth.is_none());
828-
assert_eq!(x.ops.as_ref().unwrap().size, 10);
829-
assert_eq!(x.ops.as_ref().unwrap().one_time_burst.unwrap(), 20);
830-
assert_eq!(x.ops.as_ref().unwrap().refill_time, 1000);
831-
assert_eq!(x.timer_active, false);
832-
833-
let jstr = r#"{
834-
"bandwidth": { "size": 1000, "one_time_burst": 2000, "refill_time": 1000 },
835-
"opz": { "size": 10, "one_time_burst": 20, "refill_time": 1000 }
836-
}"#;
837-
assert!(serde_json::from_str::<RateLimiter>(jstr).is_err());
838-
839-
let jstr = r#"{
840-
}"#;
841-
assert!(serde_json::from_str::<RateLimiter>(jstr).is_ok());
842-
}
843-
844750
#[test]
845751
fn test_update_buckets() {
846-
let jstr = r#"{
847-
"bandwidth": { "size": 1000, "one_time_burst": 2000, "refill_time": 1000 },
848-
"ops": { "size": 10, "one_time_burst": 20, "refill_time": 1000 }
849-
}"#;
850-
851-
let mut x: RateLimiter = serde_json::from_str(jstr).unwrap();
752+
let mut x = RateLimiter::new(1000, Some(2000), 1000, 10, Some(20), 1000).unwrap();
852753

853754
let initial_bw = x.bandwidth.clone();
854755
let initial_ops = x.ops.clone();

vmm/src/lib.rs

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -745,13 +745,21 @@ impl Vmm {
745745
let (epoll_config, handler_idx) = epoll_context.allocate_virtio_block_tokens();
746746
self.drive_handler_id_map
747747
.insert(drive_config.drive_id.clone(), handler_idx);
748+
let rate_limiter = match drive_config.rate_limiter {
749+
Some(rlim_cfg) => Some(
750+
rlim_cfg
751+
.into_rate_limiter()
752+
.map_err(StartMicrovmError::CreateRateLimiter)?,
753+
),
754+
None => None,
755+
};
748756

749757
let block_box = Box::new(
750758
devices::virtio::Block::new(
751759
block_file,
752760
drive_config.is_read_only,
753761
epoll_config,
754-
drive_config.rate_limiter.take(),
762+
rate_limiter,
755763
)
756764
.map_err(StartMicrovmError::CreateBlockDevice)?,
757765
);
@@ -783,8 +791,20 @@ impl Vmm {
783791
.insert(cfg.iface_id.clone(), handler_idx);
784792

785793
let allow_mmds_requests = cfg.allow_mmds_requests();
786-
let rx_rate_limiter = cfg.rx_rate_limiter.take();
787-
let tx_rate_limiter = cfg.tx_rate_limiter.take();
794+
let rx_rate_limiter = match cfg.rx_rate_limiter {
795+
Some(rlim) => Some(
796+
rlim.into_rate_limiter()
797+
.map_err(StartMicrovmError::CreateRateLimiter)?,
798+
),
799+
None => None,
800+
};
801+
let tx_rate_limiter = match cfg.tx_rate_limiter {
802+
Some(rlim) => Some(
803+
rlim.into_rate_limiter()
804+
.map_err(StartMicrovmError::CreateRateLimiter)?,
805+
),
806+
None => None,
807+
};
788808

789809
if let Some(tap) = cfg.take_tap() {
790810
let net_box = Box::new(
@@ -1565,41 +1585,23 @@ impl Vmm {
15651585

15661586
// Check if we need to update the RX rate limiter.
15671587
if let Some(new_rlim_cfg) = new_cfg.rx_rate_limiter {
1568-
if let Some(ref mut old_rlim) = old_cfg.rx_rate_limiter {
1588+
if let Some(ref mut old_rlim_cfg) = old_cfg.rx_rate_limiter {
15691589
// We already have an RX rate limiter set, so we'll update it.
1570-
old_rlim.update_buckets(
1571-
new_rlim_cfg.bandwidth.map(|b| b.into_token_bucket()),
1572-
new_rlim_cfg.ops.map(|b| b.into_token_bucket()),
1573-
);
1590+
old_rlim_cfg.update(&new_rlim_cfg);
15741591
} else {
15751592
// No old RX rate limiter; create one now.
1576-
old_cfg.rx_rate_limiter =
1577-
Some(new_rlim_cfg.into_rate_limiter().map_err(|e| {
1578-
VmmActionError::NetworkConfig(
1579-
ErrorKind::Internal,
1580-
NetworkInterfaceError::RateLimiterError(e),
1581-
)
1582-
})?);
1593+
old_cfg.rx_rate_limiter = Some(new_rlim_cfg);
15831594
}
15841595
}
15851596

15861597
// Check if we need to update the TX rate limiter.
15871598
if let Some(new_rlim_cfg) = new_cfg.tx_rate_limiter {
1588-
if let Some(ref mut old_rlim) = old_cfg.tx_rate_limiter {
1599+
if let Some(ref mut old_rlim_cfg) = old_cfg.tx_rate_limiter {
15891600
// We already have a TX rate limiter set, so we'll update it.
1590-
old_rlim.update_buckets(
1591-
new_rlim_cfg.bandwidth.map(|b| b.into_token_bucket()),
1592-
new_rlim_cfg.ops.map(|b| b.into_token_bucket()),
1593-
);
1601+
old_rlim_cfg.update(&new_rlim_cfg);
15941602
} else {
15951603
// No old TX rate limiter; create one now.
1596-
old_cfg.tx_rate_limiter =
1597-
Some(new_rlim_cfg.into_rate_limiter().map_err(|e| {
1598-
VmmActionError::NetworkConfig(
1599-
ErrorKind::Internal,
1600-
NetworkInterfaceError::RateLimiterError(e),
1601-
)
1602-
})?);
1604+
old_cfg.tx_rate_limiter = Some(new_rlim_cfg);
16031605
}
16041606
}
16051607

vmm/src/vmm_config/drive.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::fmt::{Display, Formatter};
77
use std::path::PathBuf;
88
use std::result;
99

10-
use rate_limiter::RateLimiter;
10+
use super::RateLimiterConfig;
1111

1212
type Result<T> = result::Result<T, DriveError>;
1313

@@ -74,7 +74,7 @@ pub struct BlockDeviceConfig {
7474
/// drive is opened as read-write.
7575
pub is_read_only: bool,
7676
/// Rate Limiter for I/O operations.
77-
pub rate_limiter: Option<RateLimiter>,
77+
pub rate_limiter: Option<RateLimiterConfig>,
7878
}
7979

8080
impl BlockDeviceConfig {

vmm/src/vmm_config/instance_info.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ pub enum StartMicrovmError {
5757
/// Internal errors are due to resource exhaustion.
5858
/// Users errors are due to invalid permissions.
5959
CreateNetDevice(devices::virtio::Error),
60+
/// Failed to create a `RateLimiter` object.
61+
CreateRateLimiter(std::io::Error),
6062
#[cfg(feature = "vsock")]
6163
/// Creating a vsock device can only fail if the /dev/vhost-vsock device cannot be open.
6264
CreateVsockDevice(devices::virtio::vhost::Error),
@@ -125,6 +127,7 @@ impl Display for StartMicrovmError {
125127
the file was deleted/corrupted. Error number: {}",
126128
err.errno().to_string()
127129
),
130+
CreateRateLimiter(ref err) => write!(f, "Cannot create RateLimiter: {}", err),
128131
#[cfg(feature = "vsock")]
129132
CreateVsockDevice(ref err) => {
130133
let mut err_msg = format!("{:?}", err);

vmm/src/vmm_config/mod.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub mod vsock;
3131

3232
/// A public-facing, stateless structure, holding all the data we need to create a TokenBucket
3333
/// (live) object.
34-
#[derive(Clone, Copy, Debug, Default, Deserialize)]
34+
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq)]
3535
pub struct TokenBucketConfig {
3636
/// See TokenBucket::size.
3737
pub size: u64,
@@ -54,7 +54,7 @@ impl TokenBucketConfig {
5454

5555
/// A public-facing, stateless structure, holding all the data we need to create a RateLimiter
5656
/// (live) object.
57-
#[derive(Clone, Copy, Debug, Default, Deserialize)]
57+
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq)]
5858
pub struct RateLimiterConfig {
5959
/// Data used to initialize the RateLimiter::bandwidth bucket.
6060
pub bandwidth: Option<TokenBucketConfig>,
@@ -76,4 +76,13 @@ impl RateLimiterConfig {
7676
ops.refill_time,
7777
)
7878
}
79+
/// Updates the configuration, merging in new options from `new_config`.
80+
pub fn update(&mut self, new_config: &RateLimiterConfig) {
81+
if new_config.bandwidth.is_some() {
82+
self.bandwidth = new_config.bandwidth;
83+
}
84+
if new_config.ops.is_some() {
85+
self.ops = new_config.ops;
86+
}
87+
}
7988
}

vmm/src/vmm_config/net.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use super::super::Error as VmmInternalError;
99
use super::RateLimiterConfig;
1010
use devices;
1111
use net_util::{MacAddr, Tap, TapError};
12-
use rate_limiter::RateLimiter;
1312

1413
/// This struct represents the strongly typed equivalent of the json body from net iface
1514
/// related requests.
@@ -23,9 +22,9 @@ pub struct NetworkInterfaceConfig {
2322
/// Guest MAC address.
2423
pub guest_mac: Option<MacAddr>,
2524
/// Rate Limiter for received packages.
26-
pub rx_rate_limiter: Option<RateLimiter>,
25+
pub rx_rate_limiter: Option<RateLimiterConfig>,
2726
/// Rate Limiter for transmitted packages.
28-
pub tx_rate_limiter: Option<RateLimiter>,
27+
pub tx_rate_limiter: Option<RateLimiterConfig>,
2928
#[serde(default = "default_allow_mmds_requests")]
3029
/// If this field is set, the device model will reply to HTTP GET
3130
/// requests sent to the MMDS address via this interface. In this case,
@@ -297,8 +296,8 @@ mod tests {
297296
iface_id: String::from(id),
298297
host_dev_name: String::from(name),
299298
guest_mac: Some(MacAddr::parse_str(mac).unwrap()),
300-
rx_rate_limiter: Some(RateLimiter::default()),
301-
tx_rate_limiter: Some(RateLimiter::default()),
299+
rx_rate_limiter: Some(RateLimiterConfig::default()),
300+
tx_rate_limiter: Some(RateLimiterConfig::default()),
302301
allow_mmds_requests: false,
303302
tap: None,
304303
}

0 commit comments

Comments
 (0)