1use crate::vmm::Topology;
7
8pub struct TopoPreset {
13 pub name: &'static str,
14 #[allow(dead_code)]
16 pub description: &'static str,
17 pub topology: Topology,
18 #[allow(dead_code)]
20 pub memory_mib: usize,
21}
22
23pub fn gauntlet_presets() -> Vec<TopoPreset> {
41 let defs: &[(&str, &str, u32, u32, u32, usize)] = &[
42 ("tiny-1llc", "4 CPUs, 1 LLC", 1, 4, 1, 2048),
43 ("tiny-2llc", "4 CPUs, 2 LLCs", 2, 2, 1, 2048),
44 ("odd-3llc", "9 CPUs, 3 LLCs (odd)", 3, 3, 1, 2048),
45 ("odd-5llc", "15 CPUs, 5 LLCs (prime)", 5, 3, 1, 2048),
46 ("odd-7llc", "14 CPUs, 7 LLCs (prime)", 7, 2, 1, 2048),
47 ("smt-2llc", "8 CPUs, 2 LLCs with SMT", 2, 2, 2, 2048),
48 ("smt-3llc", "12 CPUs, 3 LLCs with SMT", 3, 2, 2, 2048),
49 ("medium-4llc", "32 CPUs, 4 LLCs", 4, 4, 2, 2048),
50 ("medium-8llc", "64 CPUs, 8 LLCs", 8, 4, 2, 2048),
51 ("large-4llc", "128 CPUs, 4 LLCs", 4, 16, 2, 2048),
52 ("large-8llc", "128 CPUs, 8 LLCs", 8, 8, 2, 2048),
53 (
54 "near-max-llc",
55 "240 CPUs, 15 LLCs (near max)",
56 15,
57 8,
58 2,
59 2048,
60 ),
61 (
62 "max-cpu",
63 "252 CPUs, 14 LLCs (near KVM vCPU limit)",
64 14,
65 9,
66 2,
67 4096,
68 ),
69 (
72 "medium-4llc-nosmt",
73 "32 CPUs, 4 LLCs (no SMT)",
74 4,
75 8,
76 1,
77 2048,
78 ),
79 (
80 "medium-8llc-nosmt",
81 "64 CPUs, 8 LLCs (no SMT)",
82 8,
83 8,
84 1,
85 2048,
86 ),
87 (
88 "large-4llc-nosmt",
89 "128 CPUs, 4 LLCs (no SMT)",
90 4,
91 32,
92 1,
93 2048,
94 ),
95 (
96 "large-8llc-nosmt",
97 "128 CPUs, 8 LLCs (no SMT)",
98 8,
99 16,
100 1,
101 2048,
102 ),
103 (
104 "near-max-llc-nosmt",
105 "240 CPUs, 15 LLCs (no SMT)",
106 15,
107 16,
108 1,
109 2048,
110 ),
111 (
112 "max-cpu-nosmt",
113 "252 CPUs, 14 LLCs (no SMT, near KVM vCPU limit)",
114 14,
115 18,
116 1,
117 4096,
118 ),
119 ];
120 let numa_defs: &[(&str, &str, u32, u32, u32, u32, usize)] = &[
121 (
122 "numa2-4llc",
123 "16 CPUs, 2 NUMA nodes, 4 LLCs",
124 2,
125 4,
126 4,
127 1,
128 2048,
129 ),
130 (
131 "numa2-8llc",
132 "128 CPUs, 2 NUMA nodes, 8 LLCs",
133 2,
134 8,
135 8,
136 2,
137 2048,
138 ),
139 (
140 "numa2-8llc-nosmt",
141 "128 CPUs, 2 NUMA nodes, 8 LLCs (no SMT)",
142 2,
143 8,
144 16,
145 1,
146 2048,
147 ),
148 (
149 "numa4-8llc",
150 "32 CPUs, 4 NUMA nodes, 8 LLCs",
151 4,
152 8,
153 4,
154 1,
155 2048,
156 ),
157 (
158 "numa4-12llc",
159 "192 CPUs, 4 NUMA nodes, 12 LLCs",
160 4,
161 12,
162 8,
163 2,
164 4096,
165 ),
166 ];
167
168 let mut presets: Vec<TopoPreset> = defs
169 .iter()
170 .map(|&(n, d, s, c, t, m)| TopoPreset {
171 name: n,
172 description: d,
173 topology: Topology {
174 llcs: s,
175 cores_per_llc: c,
176 threads_per_core: t,
177 numa_nodes: 1,
178 nodes: None,
179 distances: None,
180 },
181 memory_mib: m,
182 })
183 .chain(numa_defs.iter().map(|&(n, d, nn, s, c, t, m)| TopoPreset {
184 name: n,
185 description: d,
186 topology: Topology {
187 llcs: s,
188 cores_per_llc: c,
189 threads_per_core: t,
190 numa_nodes: nn,
191 nodes: None,
192 distances: None,
193 },
194 memory_mib: m,
195 }))
196 .collect();
197
198 if cfg!(target_arch = "aarch64") {
200 presets.retain(|p| p.topology.threads_per_core <= 1);
201 }
202
203 presets
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn gauntlet_presets_unique_names() {
212 let p = gauntlet_presets();
213 let names: Vec<&str> = p.iter().map(|p| p.name).collect();
214 let unique: std::collections::HashSet<&&str> = names.iter().collect();
215 assert_eq!(names.len(), unique.len());
216 }
217
218 #[test]
219 fn gauntlet_presets_total_cpus_match() {
220 for p in &gauntlet_presets() {
221 let cpus = p.topology.total_cpus();
222 assert!(
223 p.description.contains(&cpus.to_string()),
224 "{}: description '{}' doesn't mention {} CPUs",
225 p.name,
226 p.description,
227 cpus
228 );
229 }
230 }
231
232 #[test]
233 fn gauntlet_presets_memory_sane() {
234 for p in &gauntlet_presets() {
235 assert!(
236 p.memory_mib >= 512,
237 "{} has too little memory: {}MiB",
238 p.name,
239 p.memory_mib
240 );
241 let cpus = p.topology.total_cpus() as usize;
242 assert!(
243 p.memory_mib >= cpus * 8,
244 "{} has {}MiB for {} CPUs",
245 p.name,
246 p.memory_mib,
247 cpus
248 );
249 }
250 }
251
252 #[test]
253 fn gauntlet_presets_topology_pinned() {
254 let expected: &[(&str, u32, u32)] = &[
256 ("tiny-1llc", 1, 4),
257 ("tiny-2llc", 2, 4),
258 ("odd-3llc", 3, 9),
259 ("odd-5llc", 5, 15),
260 ("odd-7llc", 7, 14),
261 #[cfg(not(target_arch = "aarch64"))]
262 ("smt-2llc", 2, 8),
263 #[cfg(not(target_arch = "aarch64"))]
264 ("smt-3llc", 3, 12),
265 #[cfg(not(target_arch = "aarch64"))]
266 ("medium-4llc", 4, 32),
267 #[cfg(not(target_arch = "aarch64"))]
268 ("medium-8llc", 8, 64),
269 #[cfg(not(target_arch = "aarch64"))]
270 ("large-4llc", 4, 128),
271 #[cfg(not(target_arch = "aarch64"))]
272 ("large-8llc", 8, 128),
273 #[cfg(not(target_arch = "aarch64"))]
274 ("near-max-llc", 15, 240),
275 #[cfg(not(target_arch = "aarch64"))]
276 ("max-cpu", 14, 252),
277 ("medium-4llc-nosmt", 4, 32),
278 ("medium-8llc-nosmt", 8, 64),
279 ("large-4llc-nosmt", 4, 128),
280 ("large-8llc-nosmt", 8, 128),
281 ("near-max-llc-nosmt", 15, 240),
282 ("max-cpu-nosmt", 14, 252),
283 ("numa2-4llc", 4, 16),
284 #[cfg(not(target_arch = "aarch64"))]
285 ("numa2-8llc", 8, 128),
286 ("numa2-8llc-nosmt", 8, 128),
287 ("numa4-8llc", 8, 32),
288 #[cfg(not(target_arch = "aarch64"))]
289 ("numa4-12llc", 12, 192),
290 ];
291 let presets = gauntlet_presets();
292 assert_eq!(
293 expected.len(),
294 presets.len(),
295 "pinned list and preset list have different lengths"
296 );
297 for &(name, llcs, cpus) in expected {
298 let p = presets.iter().find(|p| p.name == name).unwrap();
299 assert_eq!(
300 p.topology.num_llcs(),
301 llcs,
302 "{}: expected {} LLCs, got {}",
303 name,
304 llcs,
305 p.topology.num_llcs()
306 );
307 assert_eq!(
308 p.topology.total_cpus(),
309 cpus,
310 "{}: expected {} CPUs, got {}",
311 name,
312 cpus,
313 p.topology.total_cpus()
314 );
315 }
316 }
317
318 #[test]
319 fn gauntlet_presets_topology_valid() {
320 for p in &gauntlet_presets() {
321 p.topology
322 .validate()
323 .unwrap_or_else(|e| panic!("{}: {e}", p.name));
324 }
325 }
326
327 #[test]
328 fn gauntlet_presets_max_cpu_near_limit() {
329 let presets = gauntlet_presets();
330 let max_presets: Vec<_> = presets
331 .iter()
332 .filter(|p| p.name.starts_with("max-cpu"))
333 .collect();
334 assert!(
335 !max_presets.is_empty(),
336 "at least one max-cpu preset must exist"
337 );
338 for p in &max_presets {
339 let cpus = p.topology.total_cpus();
340 assert!(
341 cpus <= 255,
342 "{} has {} CPUs, exceeds KVM vCPU limit",
343 p.name,
344 cpus
345 );
346 assert!(
347 cpus >= 200,
348 "{} should be near the limit: {} CPUs",
349 p.name,
350 cpus
351 );
352 }
353 }
354
355 #[test]
356 fn topology_single_cpu() {
357 let t = Topology {
358 llcs: 1,
359 cores_per_llc: 1,
360 threads_per_core: 1,
361 numa_nodes: 1,
362 nodes: None,
363 distances: None,
364 };
365 assert_eq!(t.total_cpus(), 1);
366 assert_eq!(t.num_llcs(), 1);
367 }
368
369 #[test]
370 #[cfg(not(target_arch = "aarch64"))]
371 fn gauntlet_presets_smt_presets_have_threads() {
372 let presets = gauntlet_presets();
373 for p in &presets {
374 if p.name.starts_with("smt-") {
375 assert_eq!(
376 p.topology.threads_per_core, 2,
377 "{} should have 2 threads per core",
378 p.name
379 );
380 }
381 }
382 }
383
384 #[test]
385 fn gauntlet_presets_odd_presets_are_odd() {
386 let presets = gauntlet_presets();
387 for p in &presets {
388 if p.name.starts_with("odd-") {
389 assert!(
390 p.topology.llcs % 2 != 0,
391 "{}: odd-* presets must have odd LLC count, got {} LLCs",
392 p.name,
393 p.topology.llcs
394 );
395 }
396 }
397 }
398
399 #[test]
400 fn gauntlet_presets_numa_presets_have_correct_nodes() {
401 for p in &gauntlet_presets() {
402 if p.name.starts_with("numa2") {
403 assert_eq!(
404 p.topology.numa_nodes, 2,
405 "{}: expected 2 NUMA nodes",
406 p.name
407 );
408 } else if p.name.starts_with("numa4") {
409 assert_eq!(
410 p.topology.numa_nodes, 4,
411 "{}: expected 4 NUMA nodes",
412 p.name
413 );
414 }
415 }
416 }
417
418 #[test]
419 fn gauntlet_presets_description_non_empty() {
420 for p in &gauntlet_presets() {
421 assert!(
422 !p.description.is_empty(),
423 "{} has empty description",
424 p.name
425 );
426 }
427 }
428}