1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// Copyright (C) 2022 Alibaba Cloud. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

//! Structs to manage guest memory for virtual machines.
//!
//! The `vm-memory` crate only provides traits and structs to access normal guest memory,
//! it doesn't support special guest memory like virtio-fs/virtio-pmem DAX window etc.
//! So this crate provides `GuestMemoryManager` over `vm-memory` to provide uniform abstraction
//! for all guest memory.
//!
//! It also provides interfaces to coordinate guest memory hotplug events.

use std::str::FromStr;
use std::sync::Arc;
use vm_memory::{GuestAddressSpace, GuestMemoryAtomic, GuestMemoryLoadGuard, GuestMemoryMmap};

mod raw_region;
pub use raw_region::GuestRegionRaw;

mod hybrid;
pub use hybrid::{GuestMemoryHybrid, GuestRegionHybrid};

/// Type of source to allocate memory for virtual machines.
#[derive(Debug, Eq, PartialEq)]
pub enum MemorySourceType {
    /// File on HugeTlbFs.
    FileOnHugeTlbFs,
    /// mmap() without flag `MAP_HUGETLB`.
    MmapAnonymous,
    /// mmap() with flag `MAP_HUGETLB`.
    MmapAnonymousHugeTlbFs,
    /// memfd() without flag `MFD_HUGETLB`.
    MemFdShared,
    /// memfd() with flag `MFD_HUGETLB`.
    MemFdOnHugeTlbFs,
}

impl MemorySourceType {
    /// Check whether the memory source is huge page.
    pub fn is_hugepage(&self) -> bool {
        *self == Self::FileOnHugeTlbFs
            || *self == Self::MmapAnonymousHugeTlbFs
            || *self == Self::MemFdOnHugeTlbFs
    }

    /// Check whether the memory source is anonymous memory.
    pub fn is_mmap_anonymous(&self) -> bool {
        *self == Self::MmapAnonymous || *self == Self::MmapAnonymousHugeTlbFs
    }
}

impl FromStr for MemorySourceType {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "hugetlbfs" => Ok(MemorySourceType::FileOnHugeTlbFs),
            "memfd" => Ok(MemorySourceType::MemFdShared),
            "shmem" => Ok(MemorySourceType::MemFdShared),
            "hugememfd" => Ok(MemorySourceType::MemFdOnHugeTlbFs),
            "hugeshmem" => Ok(MemorySourceType::MemFdOnHugeTlbFs),
            "anon" => Ok(MemorySourceType::MmapAnonymous),
            "mmap" => Ok(MemorySourceType::MmapAnonymous),
            "hugeanon" => Ok(MemorySourceType::MmapAnonymousHugeTlbFs),
            "hugemmap" => Ok(MemorySourceType::MmapAnonymousHugeTlbFs),
            _ => Err(format!("unknown memory source type {}", s)),
        }
    }
}

#[derive(Debug, Default)]
struct GuestMemoryHotplugManager {}

/// The `GuestMemoryManager` manages all guest memory for virtual machines.
///
/// The `GuestMemoryManager` fulfills several different responsibilities.
/// - First, it manages different types of guest memory, such as normal guest memory, virtio-fs
///   DAX window and virtio-pmem DAX window etc. Different clients may want to access different
///   types of memory. So the manager maintains two GuestMemory objects, one contains all guest
///   memory, the other contains only normal guest memory.
/// - Second, it coordinates memory/DAX window hotplug events, so clients may register hooks
///   to receive hotplug notifications.
#[allow(unused)]
#[derive(Debug, Clone)]
pub struct GuestMemoryManager {
    default: GuestMemoryAtomic<GuestMemoryHybrid>,
    /// GuestMemory object hosts all guest memory.
    hybrid: GuestMemoryAtomic<GuestMemoryHybrid>,
    /// GuestMemory object for vIOMMU.
    iommu: GuestMemoryAtomic<GuestMemoryHybrid>,
    /// GuestMemory object hosts normal guest memory.
    normal: GuestMemoryAtomic<GuestMemoryMmap>,
    hotplug: Arc<GuestMemoryHotplugManager>,
}

impl GuestMemoryManager {
    /// Create a new instance of `GuestMemoryManager`.
    pub fn new() -> Self {
        Self::default()
    }

    /// Get a reference to the normal `GuestMemory` object.
    pub fn get_normal_guest_memory(&self) -> &GuestMemoryAtomic<GuestMemoryMmap> {
        &self.normal
    }

    /// Try to downcast the `GuestAddressSpace` object to a `GuestMemoryManager` object.
    pub fn to_manager<AS: GuestAddressSpace>(_m: &AS) -> Option<&Self> {
        None
    }
}

impl Default for GuestMemoryManager {
    fn default() -> Self {
        let hybrid = GuestMemoryAtomic::new(GuestMemoryHybrid::new());
        let iommu = GuestMemoryAtomic::new(GuestMemoryHybrid::new());
        let normal = GuestMemoryAtomic::new(GuestMemoryMmap::new());
        // By default, it provides to the `GuestMemoryHybrid` object containing all guest memory.
        let default = hybrid.clone();

        GuestMemoryManager {
            default,
            hybrid,
            iommu,
            normal,
            hotplug: Arc::new(GuestMemoryHotplugManager::default()),
        }
    }
}

impl GuestAddressSpace for GuestMemoryManager {
    type M = GuestMemoryHybrid;
    type T = GuestMemoryLoadGuard<GuestMemoryHybrid>;

    fn memory(&self) -> Self::T {
        self.default.memory()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_memory_source_type() {
        assert_eq!(
            MemorySourceType::from_str("hugetlbfs").unwrap(),
            MemorySourceType::FileOnHugeTlbFs
        );
        assert_eq!(
            MemorySourceType::from_str("memfd").unwrap(),
            MemorySourceType::MemFdShared
        );
        assert_eq!(
            MemorySourceType::from_str("shmem").unwrap(),
            MemorySourceType::MemFdShared
        );
        assert_eq!(
            MemorySourceType::from_str("hugememfd").unwrap(),
            MemorySourceType::MemFdOnHugeTlbFs
        );
        assert_eq!(
            MemorySourceType::from_str("hugeshmem").unwrap(),
            MemorySourceType::MemFdOnHugeTlbFs
        );
        assert_eq!(
            MemorySourceType::from_str("anon").unwrap(),
            MemorySourceType::MmapAnonymous
        );
        assert_eq!(
            MemorySourceType::from_str("mmap").unwrap(),
            MemorySourceType::MmapAnonymous
        );
        assert_eq!(
            MemorySourceType::from_str("hugeanon").unwrap(),
            MemorySourceType::MmapAnonymousHugeTlbFs
        );
        assert_eq!(
            MemorySourceType::from_str("hugemmap").unwrap(),
            MemorySourceType::MmapAnonymousHugeTlbFs
        );
        assert!(MemorySourceType::from_str("test").is_err());
    }

    #[ignore]
    #[test]
    fn test_to_manager() {
        let manager = GuestMemoryManager::new();
        let mgr = GuestMemoryManager::to_manager(&manager).unwrap();

        assert_eq!(&manager as *const _, mgr as *const _);
    }
}