1use crate::cmdline::Vtl2GpaPoolConfig;
7use crate::cmdline::Vtl2GpaPoolLookupTable;
8use igvm_defs::PAGE_SIZE_4K;
9
10const LOOKUP_TABLE_RELEASE: &[(u16, u16, u16); 39] = &[
42 (2, 96, 2),
43 (2, 98, 4),
44 (2, 100, 4),
45 (2, 104, 4),
46 (4, 108, 2),
47 (4, 110, 6),
48 (4, 112, 6),
49 (4, 118, 8),
50 (4, 130, 12),
51 (8, 140, 4),
52 (8, 148, 10),
53 (8, 170, 20),
54 (8, 176, 20),
55 (16, 70, 2), (16, 234, 12),
57 (16, 256, 20), (16, 268, 38),
59 (16, 282, 54),
60 (24, 420, 66),
61 (32, 404, 22),
62 (32, 516, 36),
63 (32, 538, 74), (48, 558, 32),
65 (48, 718, 52),
66 (48, 730, 52),
67 (48, 746, 78),
68 (64, 712, 42),
69 (64, 924, 68),
70 (64, 938, 68),
71 (96, 1030, 64),
72 (96, 1042, 114), (96, 1058, 114), (96, 1340, 102),
75 (96, 1358, 104),
76 (96, 1382, 120),
77 (112, 1566, 288),
78 (128, 1342, 84),
79 (128, 1360, 84),
80 (896, 12912, 516), ];
82
83const LOOKUP_TABLE_DEBUG: &[(u16, u16, u16); 6] = &[
90 (4, 496, 4),
91 (16, 512, 16), (32, 1024, 32),
93 (32, 1536, 128), (64, 1024, 64),
95 (128, 1024, 128),
96];
97
98const ONE_MB: u64 = 1024 * 1024;
99
100const MAX_DMA_HINT_MEM_SIZE: u64 = 0xFFFFFFFF00000;
102const PAGES_PER_2MB: u64 = 2 * ONE_MB / PAGE_SIZE_4K;
104const RATIO: u32 = 1_000;
106
107fn round_up_to_2mb(pages_4k: u64) -> u64 {
109 (pages_4k + (PAGES_PER_2MB - 1)) & !(PAGES_PER_2MB - 1)
110}
111
112pub fn vtl2_calculate_dma_hint(
114 vtl2_gpa_pool_lookup_table: Vtl2GpaPoolLookupTable,
115 vp_count: usize,
116 mem_size: u64,
117) -> u64 {
118 let mut dma_hint_4k = 0;
119 if mem_size > 0 && mem_size < MAX_DMA_HINT_MEM_SIZE {
121 let mem_size_mb = (mem_size / ONE_MB) as u32;
122 #[cfg(test)]
123 tracing::info!(?vp_count, ?mem_size_mb, "Calculating VTL2 DMA hint",);
124
125 let mut min_vtl2_memory_mb = u16::MAX; let mut max_vtl2_memory_mb = 0; let mut min_ratio_1000th = 100 * RATIO;
129 let mut max_ratio_1000th = RATIO;
130
131 let mut min_vp_count: u16 = 1; let mut max_vp_count = vp_count as u16; let lookup_table = match vtl2_gpa_pool_lookup_table {
135 Vtl2GpaPoolLookupTable::Release => LOOKUP_TABLE_RELEASE.iter(),
136 Vtl2GpaPoolLookupTable::Debug => LOOKUP_TABLE_DEBUG.iter(),
137 };
138
139 for (vp_lookup, vtl2_memory_mb, dma_hint_mb) in lookup_table.clone() {
142 match (*vp_lookup).cmp(&(vp_count as u16)) {
143 core::cmp::Ordering::Less => {
144 min_vp_count = min_vp_count.max(*vp_lookup);
146 }
147 core::cmp::Ordering::Equal => {
148 if *vtl2_memory_mb == mem_size_mb as u16 {
149 dma_hint_4k = *dma_hint_mb as u64 * ONE_MB / PAGE_SIZE_4K;
151 max_vtl2_memory_mb = *vtl2_memory_mb;
152
153 break;
154 } else {
155 min_vtl2_memory_mb = min_vtl2_memory_mb.min(*vtl2_memory_mb);
157 max_vtl2_memory_mb = max_vtl2_memory_mb.max(*vtl2_memory_mb);
158 min_ratio_1000th = min_ratio_1000th
159 .min(*vtl2_memory_mb as u32 * RATIO / *dma_hint_mb as u32);
160 max_ratio_1000th = max_ratio_1000th
161 .max(*vtl2_memory_mb as u32 * RATIO / *dma_hint_mb as u32);
162 }
163 }
164 core::cmp::Ordering::Greater => {
165 max_vp_count = max_vp_count.min(*vp_lookup);
170 }
171 }
172 }
173
174 if max_vtl2_memory_mb == 0 {
182 #[cfg(test)]
183 tracing::warn!(
184 ?min_vp_count,
185 ?max_vp_count,
186 ?min_vtl2_memory_mb,
187 ?max_vtl2_memory_mb,
188 ?min_ratio_1000th,
189 ?max_ratio_1000th,
190 "Exact match not found, extrapolating DMA hint",
191 );
192 lookup_table
193 .filter(|(vp_lookup, _, _)| {
194 *vp_lookup == min_vp_count || *vp_lookup == max_vp_count
195 })
196 .for_each(|(_vp_count, vtl2_memory_mb, dma_hint_mb)| {
197 min_vtl2_memory_mb = min_vtl2_memory_mb.min(*vtl2_memory_mb);
198 max_vtl2_memory_mb = max_vtl2_memory_mb.max(*vtl2_memory_mb);
199 min_ratio_1000th =
200 min_ratio_1000th.min(*vtl2_memory_mb as u32 * RATIO / *dma_hint_mb as u32);
201 max_ratio_1000th =
202 max_ratio_1000th.max(*vtl2_memory_mb as u32 * RATIO / *dma_hint_mb as u32);
203 });
204 }
205
206 if dma_hint_4k == 0 {
207 dma_hint_4k = (mem_size_mb as u64 * RATIO as u64 * (ONE_MB / PAGE_SIZE_4K))
209 / ((min_ratio_1000th + max_ratio_1000th) as u64 / 2u64);
210
211 dma_hint_4k = round_up_to_2mb(dma_hint_4k);
213
214 #[cfg(test)]
215 tracing::debug!(
216 ?min_vp_count,
217 ?max_vp_count,
218 ?min_vtl2_memory_mb,
219 ?max_vtl2_memory_mb,
220 ?min_ratio_1000th,
221 ?max_ratio_1000th,
222 ?dma_hint_4k,
223 "Extrapolated VTL2 DMA hint",
224 );
225
226 log::info!(
227 "Extrapolated VTL2 DMA hint: {} pages ({} MiB) for {} VPs and {} MiB VTL2 memory",
228 dma_hint_4k,
229 dma_hint_4k * PAGE_SIZE_4K / ONE_MB,
230 vp_count,
231 mem_size_mb
232 );
233 } else {
234 log::info!(
235 "Found exact VTL2 DMA hint: {} pages ({} MiB) for {} VPs and {} MiB VTL2 memory",
236 dma_hint_4k,
237 dma_hint_4k * PAGE_SIZE_4K / ONE_MB,
238 vp_count,
239 mem_size_mb
240 );
241 }
242 }
243
244 dma_hint_4k
245}
246
247pub fn pick_private_pool_size(
250 cmdline: Vtl2GpaPoolConfig,
251 dt: Option<u64>,
252 vp_count: usize,
253 mem_size: u64,
254) -> Option<u64> {
255 match (cmdline, dt) {
256 (Vtl2GpaPoolConfig::Off, _) => {
257 log::info!("vtl2 gpa pool disabled via command line");
259 None
260 }
261 (Vtl2GpaPoolConfig::Pages(cmd_line_pages), _) => {
262 log::info!(
264 "vtl2 gpa pool enabled via command line with pages: {}",
265 cmd_line_pages
266 );
267 Some(cmd_line_pages)
268 }
269 (Vtl2GpaPoolConfig::Heuristics(table), None)
270 | (Vtl2GpaPoolConfig::Heuristics(table), Some(0)) => {
271 log::info!("vtl2 gpa pool coming from heuristics table: {:?}", table);
273 Some(vtl2_calculate_dma_hint(table, vp_count, mem_size))
274 }
275 (Vtl2GpaPoolConfig::Heuristics(_), Some(dt_page_count)) => {
276 log::info!(
279 "vtl2 gpa pool enabled via device tree with pages: {}",
280 dt_page_count
281 );
282 Some(dt_page_count)
283 }
284 }
285}
286
287#[cfg(test)]
288mod test {
289 use super::*;
290 use test_with_tracing::test;
291
292 const ONE_MB: u64 = 0x10_0000;
293
294 #[test]
295 fn test_vtl2_calculate_dma_hint_release() {
296 assert_eq!(
297 vtl2_calculate_dma_hint(Vtl2GpaPoolLookupTable::Release, 2, 0x620_0000),
298 4 * ONE_MB / PAGE_SIZE_4K
299 );
300 assert_eq!(
301 vtl2_calculate_dma_hint(Vtl2GpaPoolLookupTable::Release, 4, 0x6E0_0000),
302 6 * ONE_MB / PAGE_SIZE_4K
303 );
304
305 assert_eq!(
307 vtl2_calculate_dma_hint(Vtl2GpaPoolLookupTable::Release, 112, 0x700_0000),
308 22 * ONE_MB / PAGE_SIZE_4K
309 );
310
311 assert_eq!(
313 vtl2_calculate_dma_hint(Vtl2GpaPoolLookupTable::Release, 52, 0x600_0000),
314 8 * ONE_MB / PAGE_SIZE_4K
315 );
316 assert_eq!(
317 vtl2_calculate_dma_hint(Vtl2GpaPoolLookupTable::Release, 52, 0x800_0000),
318 10 * ONE_MB / PAGE_SIZE_4K
319 );
320 }
321
322 #[test]
323 fn test_vtl2_calculate_dma_hint_debug() {
324 assert_eq!(
325 vtl2_calculate_dma_hint(Vtl2GpaPoolLookupTable::Debug, 4, 496 * ONE_MB),
326 4 * ONE_MB / PAGE_SIZE_4K
327 );
328 assert_eq!(
329 vtl2_calculate_dma_hint(Vtl2GpaPoolLookupTable::Debug, 64, 1024 * ONE_MB),
330 64 * ONE_MB / PAGE_SIZE_4K
331 );
332 assert_eq!(
333 vtl2_calculate_dma_hint(Vtl2GpaPoolLookupTable::Debug, 128, 1024 * ONE_MB),
334 128 * ONE_MB / PAGE_SIZE_4K
335 );
336 assert_eq!(
338 vtl2_calculate_dma_hint(Vtl2GpaPoolLookupTable::Debug, 128, 2048 * ONE_MB),
339 256 * ONE_MB / PAGE_SIZE_4K
340 );
341 }
342
343 #[test]
344 fn test_vtl2_calculate_dma_hint_exact_matches() {
345 for (mode, table) in [
346 (Vtl2GpaPoolLookupTable::Release, LOOKUP_TABLE_RELEASE.iter()),
347 (Vtl2GpaPoolLookupTable::Debug, LOOKUP_TABLE_DEBUG.iter()),
348 ] {
349 for (vp_count, vtl2_memory_mb, dma_hint_mb) in table {
350 let calculated_dma_hint_4k = vtl2_calculate_dma_hint(
351 mode,
352 *vp_count as usize,
353 (*vtl2_memory_mb as u64) * ONE_MB,
354 );
355 let expected_dma_hint_4k = (*dma_hint_mb as u64) * ONE_MB / PAGE_SIZE_4K;
356 assert_eq!(
357 calculated_dma_hint_4k, expected_dma_hint_4k,
358 "Failed exact match test for vp_count={}, vtl2_memory_mb={}",
359 vp_count, vtl2_memory_mb
360 );
361 }
362 }
363 }
364
365 #[test]
366 fn test_right_pages_source() {
367 assert_ne!(
369 vtl2_calculate_dma_hint(Vtl2GpaPoolLookupTable::Release, 16, 256 * ONE_MB),
370 1500
371 );
372 assert_ne!(
373 vtl2_calculate_dma_hint(Vtl2GpaPoolLookupTable::Debug, 16, 256 * ONE_MB),
374 1500
375 );
376
377 for (cmdline, dt, expected) in [
378 (Vtl2GpaPoolConfig::Off, Some(1000), None),
379 (Vtl2GpaPoolConfig::Pages(2000), Some(1000), Some(2000)),
380 (Vtl2GpaPoolConfig::Pages(2000), None, Some(2000)),
381 (
382 Vtl2GpaPoolConfig::Heuristics(Vtl2GpaPoolLookupTable::Release),
383 Some(1500),
384 Some(1500), ),
386 (
387 Vtl2GpaPoolConfig::Heuristics(Vtl2GpaPoolLookupTable::Debug),
388 Some(0),
389 Some(vtl2_calculate_dma_hint(
390 Vtl2GpaPoolLookupTable::Debug,
391 16,
392 256 * ONE_MB,
393 )),
394 ),
395 (
396 Vtl2GpaPoolConfig::Heuristics(Vtl2GpaPoolLookupTable::Debug),
397 None,
398 Some(vtl2_calculate_dma_hint(
399 Vtl2GpaPoolLookupTable::Debug,
400 16,
401 256 * ONE_MB,
402 )),
403 ),
404 (
405 Vtl2GpaPoolConfig::Heuristics(Vtl2GpaPoolLookupTable::Release),
406 Some(0),
407 Some(vtl2_calculate_dma_hint(
408 Vtl2GpaPoolLookupTable::Release,
409 16,
410 256 * ONE_MB,
411 )),
412 ),
413 (
414 Vtl2GpaPoolConfig::Heuristics(Vtl2GpaPoolLookupTable::Release),
415 None,
416 Some(vtl2_calculate_dma_hint(
417 Vtl2GpaPoolLookupTable::Release,
418 16,
419 256 * ONE_MB,
420 )),
421 ),
422 ] {
423 let result = pick_private_pool_size(cmdline, dt, 16, 256 * ONE_MB);
424 assert_eq!(
425 result, expected,
426 "Failed pick_private_pool_size test for cmdline={:?}, dt={:?}",
427 cmdline, dt
428 );
429 }
430 }
431}