vmm/vmm_config/
memory_hotplug.rs1use serde::{Deserialize, Serialize};
5
6use crate::devices::virtio::mem::{
7 VIRTIO_MEM_DEFAULT_BLOCK_SIZE_MIB, VIRTIO_MEM_DEFAULT_SLOT_SIZE_MIB,
8};
9
10#[derive(Debug, thiserror::Error, displaydoc::Display)]
12pub enum MemoryHotplugConfigError {
13 BlockSizeTooSmall(usize),
15 BlockSizeNotPowerOfTwo,
17 SlotSizeTooSmall(usize),
19 SlotSizeNotMultipleOfBlockSize(usize),
21 TotalSizeTooSmall(usize),
23 TotalSizeNotMultipleOfSlotSize(usize),
25}
26
27fn default_block_size_mib() -> usize {
28 VIRTIO_MEM_DEFAULT_BLOCK_SIZE_MIB
29}
30
31fn default_slot_size_mib() -> usize {
32 VIRTIO_MEM_DEFAULT_SLOT_SIZE_MIB
33}
34
35#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
37#[serde(deny_unknown_fields)]
38pub struct MemoryHotplugConfig {
39 pub total_size_mib: usize,
41 #[serde(default = "default_block_size_mib")]
43 pub block_size_mib: usize,
44 #[serde(default = "default_slot_size_mib")]
46 pub slot_size_mib: usize,
47}
48
49impl MemoryHotplugConfig {
50 pub fn validate(&self) -> Result<(), MemoryHotplugConfigError> {
52 let min_block_size_mib = VIRTIO_MEM_DEFAULT_BLOCK_SIZE_MIB;
53 if self.block_size_mib < min_block_size_mib {
54 return Err(MemoryHotplugConfigError::BlockSizeTooSmall(
55 min_block_size_mib,
56 ));
57 }
58 if !self.block_size_mib.is_power_of_two() {
59 return Err(MemoryHotplugConfigError::BlockSizeNotPowerOfTwo);
60 }
61
62 let min_slot_size_mib = VIRTIO_MEM_DEFAULT_SLOT_SIZE_MIB;
63 if self.slot_size_mib < min_slot_size_mib {
64 return Err(MemoryHotplugConfigError::SlotSizeTooSmall(
65 min_slot_size_mib,
66 ));
67 }
68 if !self.slot_size_mib.is_multiple_of(self.block_size_mib) {
69 return Err(MemoryHotplugConfigError::SlotSizeNotMultipleOfBlockSize(
70 self.block_size_mib,
71 ));
72 }
73
74 if self.total_size_mib < self.slot_size_mib {
75 return Err(MemoryHotplugConfigError::TotalSizeTooSmall(
76 self.slot_size_mib,
77 ));
78 }
79 if !self.total_size_mib.is_multiple_of(self.slot_size_mib) {
80 return Err(MemoryHotplugConfigError::TotalSizeNotMultipleOfSlotSize(
81 self.slot_size_mib,
82 ));
83 }
84
85 Ok(())
86 }
87}
88
89#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
91#[serde(deny_unknown_fields)]
92pub struct MemoryHotplugSizeUpdate {
93 pub requested_size_mib: usize,
95}
96
97#[cfg(test)]
98mod tests {
99 use serde_json;
100
101 use super::*;
102
103 #[test]
104 fn test_valid_config() {
105 let config = MemoryHotplugConfig {
106 total_size_mib: 1024,
107 block_size_mib: 2,
108 slot_size_mib: 128,
109 };
110 config.validate().unwrap();
111 }
112
113 #[test]
114 fn test_block_size_too_small() {
115 let config = MemoryHotplugConfig {
116 total_size_mib: 1024,
117 block_size_mib: 1,
118 slot_size_mib: 128,
119 };
120 match config.validate() {
121 Err(MemoryHotplugConfigError::BlockSizeTooSmall(min)) => assert_eq!(min, 2),
122 _ => panic!("Expected InvalidBlockSizeTooSmall error"),
123 }
124 }
125
126 #[test]
127 fn test_block_size_not_power_of_two() {
128 let config = MemoryHotplugConfig {
129 total_size_mib: 1024,
130 block_size_mib: 3,
131 slot_size_mib: 128,
132 };
133 match config.validate() {
134 Err(MemoryHotplugConfigError::BlockSizeNotPowerOfTwo) => {}
135 _ => panic!("Expected InvalidBlockSizePowerOfTwo error"),
136 }
137 }
138
139 #[test]
140 fn test_slot_size_too_small() {
141 let config = MemoryHotplugConfig {
142 total_size_mib: 1024,
143 block_size_mib: 2,
144 slot_size_mib: 1,
145 };
146 match config.validate() {
147 Err(MemoryHotplugConfigError::SlotSizeTooSmall(min)) => assert_eq!(min, 128),
148 _ => panic!("Expected InvalidSlotSizeTooSmall error"),
149 }
150 }
151
152 #[test]
153 fn test_slot_size_not_multiple_of_block_size() {
154 let config = MemoryHotplugConfig {
155 total_size_mib: 1024,
156 block_size_mib: 4,
157 slot_size_mib: 130,
158 };
159 match config.validate() {
160 Err(MemoryHotplugConfigError::SlotSizeNotMultipleOfBlockSize(block_size)) => {
161 assert_eq!(block_size, 4)
162 }
163 _ => panic!("Expected InvalidSlotSizeMultiple error"),
164 }
165 }
166
167 #[test]
168 fn test_total_size_too_small() {
169 let config = MemoryHotplugConfig {
170 total_size_mib: 64,
171 block_size_mib: 2,
172 slot_size_mib: 128,
173 };
174 match config.validate() {
175 Err(MemoryHotplugConfigError::TotalSizeTooSmall(slot_size)) => {
176 assert_eq!(slot_size, 128)
177 }
178 _ => panic!("Expected InvalidTotalSizeTooSmall error"),
179 }
180 }
181
182 #[test]
183 fn test_total_size_not_multiple_of_slot_size() {
184 let config = MemoryHotplugConfig {
185 total_size_mib: 1000,
186 block_size_mib: 2,
187 slot_size_mib: 128,
188 };
189 match config.validate() {
190 Err(MemoryHotplugConfigError::TotalSizeNotMultipleOfSlotSize(slot_size)) => {
191 assert_eq!(slot_size, 128)
192 }
193 _ => panic!("Expected InvalidTotalSizeMultiple error"),
194 }
195 }
196
197 #[test]
198 fn test_defaults() {
199 assert_eq!(default_block_size_mib(), 2);
200 assert_eq!(default_slot_size_mib(), 128);
201
202 let json = r#"{
203 "total_size_mib": 1024
204 }"#;
205 let deserialized: MemoryHotplugConfig = serde_json::from_str(json).unwrap();
206 assert_eq!(
207 deserialized,
208 MemoryHotplugConfig {
209 total_size_mib: 1024,
210 block_size_mib: 2,
211 slot_size_mib: 128,
212 }
213 );
214 }
215
216 #[test]
217 fn test_serde() {
218 let config = MemoryHotplugConfig {
219 total_size_mib: 1024,
220 block_size_mib: 4,
221 slot_size_mib: 256,
222 };
223 let json = serde_json::to_string(&config).unwrap();
224 let deserialized: MemoryHotplugConfig = serde_json::from_str(&json).unwrap();
225 assert_eq!(config, deserialized);
226 }
227}