vmm/vmm_config/
mod.rs

1// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::convert::{From, TryInto};
5use std::io;
6
7use serde::{Deserialize, Serialize};
8
9use crate::rate_limiter::{BucketUpdate, RateLimiter, TokenBucket};
10
11/// Wrapper for configuring the balloon device.
12pub mod balloon;
13/// Wrapper for configuring the microVM boot source.
14pub mod boot_source;
15/// Wrapper for configuring the block devices.
16pub mod drive;
17/// Wrapper for configuring the entropy device attached to the microVM.
18pub mod entropy;
19/// Wrapper over the microVM general information attached to the microVM.
20pub mod instance_info;
21/// Wrapper for configuring the memory and CPU of the microVM.
22pub mod machine_config;
23/// Wrapper for configuring memory hotplug.
24pub mod memory_hotplug;
25/// Wrapper for configuring the metrics.
26pub mod metrics;
27/// Wrapper for configuring the MMDS.
28pub mod mmds;
29/// Wrapper for configuring the network devices attached to the microVM.
30pub mod net;
31/// Wrapper for configuring the pmem devises attached to the microVM.
32pub mod pmem;
33/// Wrapper for configuring microVM snapshots and the microVM state.
34pub mod serial;
35pub mod snapshot;
36/// Wrapper for configuring the vsock devices attached to the microVM.
37pub mod vsock;
38
39// TODO: Migrate the VMM public-facing code (i.e. interface) to use stateless structures,
40// for receiving data/args, such as the below `RateLimiterConfig` and `TokenBucketConfig`.
41// Also todo: find a better suffix than `Config`; it should illustrate the static nature
42// of the enclosed data.
43// Currently, data is passed around using live/stateful objects. Switching to static/stateless
44// objects will simplify both the ownership model and serialization.
45// Public access would then be more tightly regulated via `VmmAction`s, consisting of tuples like
46// (entry-point-into-VMM-logic, stateless-args-structure).
47
48/// A public-facing, stateless structure, holding all the data we need to create a TokenBucket
49/// (live) object.
50#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
51pub struct TokenBucketConfig {
52    /// See TokenBucket::size.
53    pub size: u64,
54    /// See TokenBucket::one_time_burst.
55    pub one_time_burst: Option<u64>,
56    /// See TokenBucket::refill_time.
57    pub refill_time: u64,
58}
59
60impl From<&TokenBucket> for TokenBucketConfig {
61    fn from(tb: &TokenBucket) -> Self {
62        let one_time_burst = match tb.initial_one_time_burst() {
63            0 => None,
64            v => Some(v),
65        };
66        TokenBucketConfig {
67            size: tb.capacity(),
68            one_time_burst,
69            refill_time: tb.refill_time_ms(),
70        }
71    }
72}
73
74/// A public-facing, stateless structure, holding all the data we need to create a RateLimiter
75/// (live) object.
76#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
77#[serde(deny_unknown_fields)]
78pub struct RateLimiterConfig {
79    /// Data used to initialize the RateLimiter::bandwidth bucket.
80    pub bandwidth: Option<TokenBucketConfig>,
81    /// Data used to initialize the RateLimiter::ops bucket.
82    pub ops: Option<TokenBucketConfig>,
83}
84
85/// A public-facing, stateless structure, specifying RateLimiter properties updates.
86#[derive(Debug)]
87pub struct RateLimiterUpdate {
88    /// Possible update to the RateLimiter::bandwidth bucket.
89    pub bandwidth: BucketUpdate,
90    /// Possible update to the RateLimiter::ops bucket.
91    pub ops: BucketUpdate,
92}
93
94fn get_bucket_update(tb_cfg: &Option<TokenBucketConfig>) -> BucketUpdate {
95    match tb_cfg {
96        // There is data to update.
97        Some(tb_cfg) => {
98            TokenBucket::new(
99                tb_cfg.size,
100                tb_cfg.one_time_burst.unwrap_or(0),
101                tb_cfg.refill_time,
102            )
103            // Updated active rate-limiter.
104            .map(BucketUpdate::Update)
105            // Updated/deactivated rate-limiter
106            .unwrap_or(BucketUpdate::Disabled)
107        }
108        // No update to the rate-limiter.
109        None => BucketUpdate::None,
110    }
111}
112
113impl From<Option<RateLimiterConfig>> for RateLimiterUpdate {
114    fn from(cfg: Option<RateLimiterConfig>) -> Self {
115        if let Some(cfg) = cfg {
116            RateLimiterUpdate {
117                bandwidth: get_bucket_update(&cfg.bandwidth),
118                ops: get_bucket_update(&cfg.ops),
119            }
120        } else {
121            // No update to the rate-limiter.
122            RateLimiterUpdate {
123                bandwidth: BucketUpdate::None,
124                ops: BucketUpdate::None,
125            }
126        }
127    }
128}
129
130impl TryInto<RateLimiter> for RateLimiterConfig {
131    type Error = io::Error;
132
133    fn try_into(self) -> Result<RateLimiter, Self::Error> {
134        let bw = self.bandwidth.unwrap_or_default();
135        let ops = self.ops.unwrap_or_default();
136        RateLimiter::new(
137            bw.size,
138            bw.one_time_burst.unwrap_or(0),
139            bw.refill_time,
140            ops.size,
141            ops.one_time_burst.unwrap_or(0),
142            ops.refill_time,
143        )
144    }
145}
146
147impl From<&RateLimiter> for RateLimiterConfig {
148    fn from(rl: &RateLimiter) -> Self {
149        RateLimiterConfig {
150            bandwidth: rl.bandwidth().map(TokenBucketConfig::from),
151            ops: rl.ops().map(TokenBucketConfig::from),
152        }
153    }
154}
155
156impl RateLimiterConfig {
157    /// [`Option<T>`] already implements [`From<T>`] so we have to use a custom
158    /// one.
159    pub fn into_option(self) -> Option<RateLimiterConfig> {
160        if self.bandwidth.is_some() || self.ops.is_some() {
161            Some(self)
162        } else {
163            None
164        }
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171
172    const SIZE: u64 = 1024 * 1024;
173    const ONE_TIME_BURST: u64 = 1024;
174    const REFILL_TIME: u64 = 1000;
175
176    #[test]
177    fn test_rate_limiter_configs() {
178        let rlconf = RateLimiterConfig {
179            bandwidth: Some(TokenBucketConfig {
180                size: SIZE,
181                one_time_burst: Some(ONE_TIME_BURST),
182                refill_time: REFILL_TIME,
183            }),
184            ops: Some(TokenBucketConfig {
185                size: SIZE * 2,
186                one_time_burst: None,
187                refill_time: REFILL_TIME * 2,
188            }),
189        };
190        let rl: RateLimiter = rlconf.try_into().unwrap();
191        assert_eq!(rl.bandwidth().unwrap().capacity(), SIZE);
192        assert_eq!(rl.bandwidth().unwrap().one_time_burst(), ONE_TIME_BURST);
193        assert_eq!(rl.bandwidth().unwrap().refill_time_ms(), REFILL_TIME);
194        assert_eq!(rl.ops().unwrap().capacity(), SIZE * 2);
195        assert_eq!(rl.ops().unwrap().one_time_burst(), 0);
196        assert_eq!(rl.ops().unwrap().refill_time_ms(), REFILL_TIME * 2);
197    }
198
199    #[test]
200    fn test_generate_configs() {
201        let bw_tb_cfg = TokenBucketConfig {
202            size: SIZE,
203            one_time_burst: Some(ONE_TIME_BURST),
204            refill_time: REFILL_TIME,
205        };
206        let bw_tb = TokenBucket::new(SIZE, ONE_TIME_BURST, REFILL_TIME).unwrap();
207        let generated_bw_tb_cfg = TokenBucketConfig::from(&bw_tb);
208        assert_eq!(generated_bw_tb_cfg, bw_tb_cfg);
209
210        let rl_conf = RateLimiterConfig {
211            bandwidth: Some(bw_tb_cfg),
212            ops: None,
213        };
214        let rl: RateLimiter = rl_conf.try_into().unwrap();
215        let generated_rl_conf = RateLimiterConfig::from(&rl);
216        assert_eq!(generated_rl_conf, rl_conf);
217        assert_eq!(generated_rl_conf.into_option(), Some(rl_conf));
218    }
219}