Skip to main content

petri/vm/openvmm/
hugetlb.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Helpers for OpenVMM hugetlb-backed memory tests.
5
6use anyhow::Context;
7
8/// Size of a 2 MiB hugetlb page.
9pub const HUGETLB_2MB_PAGE_SIZE: u64 = 2 * 1024 * 1024;
10
11const REQUIRE_2MB_HUGETLB_ENV: &str = "OPENVMM_REQUIRE_2MB_HUGETLB";
12
13fn require_2mb_hugetlb() -> bool {
14    std::env::var_os(REQUIRE_2MB_HUGETLB_ENV).is_some()
15}
16
17fn read_hugetlb_counter(name: &str) -> anyhow::Result<Option<u64>> {
18    let path = format!("/sys/kernel/mm/hugepages/hugepages-2048kB/{name}");
19    let value = match std::fs::read_to_string(&path) {
20        Ok(value) => value,
21        Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None),
22        Err(error) => return Err(error).with_context(|| format!("failed to read {path}")),
23    };
24    Ok(Some(
25        value
26            .trim()
27            .parse()
28            .with_context(|| format!("failed to parse {path}"))?,
29    ))
30}
31
32fn available_2mb_hugetlb_pages() -> anyhow::Result<Option<u64>> {
33    let Some(free_pages) = read_hugetlb_counter("free_hugepages")? else {
34        return Ok(None);
35    };
36    let Some(overcommit_pages) = read_hugetlb_counter("nr_overcommit_hugepages")? else {
37        return Ok(None);
38    };
39    let Some(surplus_pages) = read_hugetlb_counter("surplus_hugepages")? else {
40        return Ok(None);
41    };
42
43    Ok(Some(
44        free_pages + overcommit_pages.saturating_sub(surplus_pages),
45    ))
46}
47
48/// Returns whether the host appears to have enough 2 MiB hugetlb pages available.
49///
50/// By default, missing or insufficient host support returns `Ok(false)` after
51/// logging a clear warning so local developer runs can skip tests cleanly. If
52/// `OPENVMM_REQUIRE_2MB_HUGETLB` is set, missing or insufficient host support is
53/// an error.
54pub fn ensure_2mb_hugetlb_pages(required_pages: u64) -> anyhow::Result<bool> {
55    let message = match available_2mb_hugetlb_pages()? {
56        Some(available_pages) if available_pages >= required_pages => return Ok(true),
57        Some(available_pages) => {
58            format!(
59                "host has {available_pages} available 2 MiB hugetlb pages, but {required_pages} are required; configure /sys/kernel/mm/hugepages/hugepages-2048kB/nr_overcommit_hugepages before running this test"
60            )
61        }
62        None => "host does not have 2 MiB hugetlb support configured".into(),
63    };
64
65    if require_2mb_hugetlb() {
66        anyhow::bail!(message);
67    }
68    tracing::warn!(message);
69    Ok(false)
70}