vmm/vmm_config/
balloon.rs1use std::sync::{Arc, Mutex};
5
6use serde::{Deserialize, Serialize};
7
8pub use crate::devices::virtio::balloon::BALLOON_DEV_ID;
9pub use crate::devices::virtio::balloon::device::BalloonStats;
10use crate::devices::virtio::balloon::{Balloon, BalloonConfig};
11
12type MutexBalloon = Arc<Mutex<Balloon>>;
13
14#[derive(Debug, derive_more::From, thiserror::Error, displaydoc::Display)]
16pub enum BalloonConfigError {
17 DeviceNotFound,
19 TooManyPagesRequested,
21 CreateFailure(crate::devices::virtio::balloon::BalloonError),
23}
24
25#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
28#[serde(deny_unknown_fields)]
29pub struct BalloonDeviceConfig {
30 pub amount_mib: u32,
32 pub deflate_on_oom: bool,
34 #[serde(default)]
36 pub stats_polling_interval_s: u16,
37 #[serde(default)]
39 pub free_page_hinting: bool,
40 #[serde(default)]
42 pub free_page_reporting: bool,
43}
44
45impl From<BalloonConfig> for BalloonDeviceConfig {
46 fn from(state: BalloonConfig) -> Self {
47 BalloonDeviceConfig {
48 amount_mib: state.amount_mib,
49 deflate_on_oom: state.deflate_on_oom,
50 stats_polling_interval_s: state.stats_polling_interval_s,
51 free_page_hinting: state.free_page_hinting,
52 free_page_reporting: state.free_page_reporting,
53 }
54 }
55}
56
57#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
60#[serde(deny_unknown_fields)]
61pub struct BalloonUpdateConfig {
62 pub amount_mib: u32,
64}
65
66#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
71#[serde(deny_unknown_fields)]
72pub struct BalloonUpdateStatsConfig {
73 pub stats_polling_interval_s: u16,
75}
76
77#[cfg_attr(not(test), derive(Default))]
79#[derive(Debug)]
80pub struct BalloonBuilder {
81 inner: Option<MutexBalloon>,
82}
83
84impl BalloonBuilder {
85 pub fn new() -> Self {
87 Self { inner: None }
88 }
89
90 pub fn set(&mut self, cfg: BalloonDeviceConfig) -> Result<(), BalloonConfigError> {
93 self.inner = Some(Arc::new(Mutex::new(Balloon::new(
94 cfg.amount_mib,
95 cfg.deflate_on_oom,
96 cfg.stats_polling_interval_s,
97 cfg.free_page_hinting,
98 cfg.free_page_reporting,
99 )?)));
100
101 Ok(())
102 }
103
104 pub fn set_device(&mut self, balloon: MutexBalloon) {
106 self.inner = Some(balloon);
107 }
108
109 pub fn get(&self) -> Option<&MutexBalloon> {
111 self.inner.as_ref()
112 }
113
114 pub fn get_config(&self) -> Result<BalloonDeviceConfig, BalloonConfigError> {
116 self.get()
117 .ok_or(BalloonConfigError::DeviceNotFound)
118 .map(|balloon_mutex| balloon_mutex.lock().expect("Poisoned lock").config())
119 .map(BalloonDeviceConfig::from)
120 }
121}
122
123#[cfg(test)]
124impl Default for BalloonBuilder {
125 fn default() -> BalloonBuilder {
126 let mut balloon = BalloonBuilder::new();
127 balloon.set(BalloonDeviceConfig::default()).unwrap();
128 balloon
129 }
130}
131
132#[cfg(test)]
133pub(crate) mod tests {
134 use super::*;
135
136 pub(crate) fn default_config() -> BalloonDeviceConfig {
137 BalloonDeviceConfig {
138 amount_mib: 0,
139 deflate_on_oom: false,
140 stats_polling_interval_s: 0,
141 free_page_hinting: false,
142 free_page_reporting: false,
143 }
144 }
145
146 #[test]
147 fn test_balloon_create() {
148 let default_balloon_config = default_config();
149 let balloon_config = BalloonDeviceConfig {
150 amount_mib: 0,
151 deflate_on_oom: false,
152 stats_polling_interval_s: 0,
153 free_page_hinting: false,
154 free_page_reporting: false,
155 };
156 assert_eq!(default_balloon_config, balloon_config);
157 let mut builder = BalloonBuilder::new();
158 assert!(builder.get().is_none());
159
160 builder.set(balloon_config).unwrap();
161 assert_eq!(builder.get().unwrap().lock().unwrap().num_pages(), 0);
162 assert_eq!(builder.get_config().unwrap(), default_balloon_config);
163
164 let _update_config = BalloonUpdateConfig { amount_mib: 5 };
165 let _stats_update_config = BalloonUpdateStatsConfig {
166 stats_polling_interval_s: 5,
167 };
168 }
169
170 #[test]
171 fn test_from_balloon_state() {
172 let expected_balloon_config = BalloonDeviceConfig {
173 amount_mib: 5,
174 deflate_on_oom: false,
175 stats_polling_interval_s: 3,
176 free_page_hinting: false,
177 free_page_reporting: false,
178 };
179
180 let actual_balloon_config = BalloonDeviceConfig::from(BalloonConfig {
181 amount_mib: 5,
182 deflate_on_oom: false,
183 stats_polling_interval_s: 3,
184 free_page_hinting: false,
185 free_page_reporting: false,
186 });
187
188 assert_eq!(expected_balloon_config, actual_balloon_config);
189 }
190
191 #[test]
192 fn test_set_device() {
193 let mut builder = BalloonBuilder::new();
194 let balloon = Balloon::new(0, true, 0, false, false).unwrap();
195 builder.set_device(Arc::new(Mutex::new(balloon)));
196 assert!(builder.inner.is_some());
197 }
198}