From 31237dbcd50e5d1baa3d037552eb0c0bea0022f2 Mon Sep 17 00:00:00 2001
From: Yang Xiaohe <yangxiaohe2@huawei.com>
Date: Wed, 4 Nov 2020 11:04:05 +0800
Subject: [PATCH] address_space: add FileBackend that backs memory

1. Add FileBackend, which enables file mem-mapping.
2. add FileBackend test.

Signed-off-by: Xiaohe Yang <yangxiaohe2@huawei.com>
---
 address_space/src/address_space.rs   |   6 +-
 address_space/src/host_mmap.rs       | 129 ++++++++++++++++++++++++---
 address_space/src/lib.rs             |   6 +-
 address_space/src/listener.rs        |   3 +-
 address_space/src/region.rs          |   5 +-
 boot_loader/src/x86_64/bootparam.rs  |   3 +-
 boot_loader/src/x86_64/mod.rs        |   3 +-
 device_model/src/micro_vm/mod.rs     |   3 +-
 device_model/src/mmio/virtio_mmio.rs |   5 +-
 device_model/src/virtio/queue.rs     |   5 +-
 10 files changed, 139 insertions(+), 29 deletions(-)

diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs
index e87ccfa..4a53de3 100644
--- a/address_space/src/address_space.rs
+++ b/address_space/src/address_space.rs
@@ -617,8 +617,8 @@ mod test {
             write: Arc::new(|_: &[u8], _: GuestAddress, _: u64| -> bool { true }),
         };
 
-        let ram1 = Arc::new(HostMemMapping::new(GuestAddress(0), 1000, false).unwrap());
-        let ram2 = Arc::new(HostMemMapping::new(GuestAddress(2000), 1000, false).unwrap());
+        let ram1 = Arc::new(HostMemMapping::new(GuestAddress(0), 1000, -1, 0, false).unwrap());
+        let ram2 = Arc::new(HostMemMapping::new(GuestAddress(2000), 1000, -1, 0, false).unwrap());
         let region_a = Region::init_ram_region(ram1.clone());
         let region_b = Region::init_ram_region(ram2.clone());
         root.add_subregion(region_a, ram1.start_address().raw_value())
@@ -681,7 +681,7 @@ mod test {
     fn test_write_and_read_object() {
         let root = Region::init_container_region(8000);
         let space = AddressSpace::new(root.clone()).unwrap();
-        let ram1 = Arc::new(HostMemMapping::new(GuestAddress(0), 1000, false).unwrap());
+        let ram1 = Arc::new(HostMemMapping::new(GuestAddress(0), 1000, -1, 0, false).unwrap());
         let region_a = Region::init_ram_region(ram1.clone());
         root.add_subregion(region_a, ram1.start_address().raw_value())
             .unwrap();
diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs
index 0a781b7..25a79fd 100644
--- a/address_space/src/host_mmap.rs
+++ b/address_space/src/host_mmap.rs
@@ -10,29 +10,114 @@
 // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 // See the Mulan PSL v2 for more details.
 
+use std::fs::File;
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
 use std::sync::Arc;
 
-use crate::errors::{ErrorKind, Result};
+use crate::errors::{ErrorKind, Result, ResultExt};
 use crate::{AddressRange, GuestAddress};
 
-/// Create a new HostMemMapping.
+/// FileBackend represents backend-file of `HostMemMapping`.
+pub struct FileBackend {
+    /// File we used to map memory.
+    pub file: File,
+    /// Offset from where the file begins.
+    pub offset: u64,
+}
+
+impl FileBackend {
+    /// Construct a new FileBackend according to path and length.
+    /// If the file is already created, this function does not change its length.
+    ///
+    /// # Arguments
+    ///
+    /// * `file_path` - The path of file.
+    /// * `file_len` - The size of file.
+    ///
+    /// # Errors
+    ///
+    /// Return Error if
+    /// * fail to create the file.
+    /// * fail to open the file.
+    /// * fail to set file length.
+    pub fn new(file_path: &str, file_len: u64) -> Result<FileBackend> {
+        let path = std::path::Path::new(&file_path);
+        let file = if path.is_dir() {
+            let fs_path = format!("{}{}", file_path, "/stratovirt_backmem_XXXXXX");
+            let fs_cstr = std::ffi::CString::new(fs_path).unwrap().into_raw();
+
+            let raw_fd = unsafe { libc::mkstemp(fs_cstr) };
+            if raw_fd < 0 {
+                return Err(std::io::Error::last_os_error())
+                    .chain_err(|| "Create file-backend failed");
+            }
+
+            unsafe { libc::unlink(fs_cstr) };
+            unsafe { File::from_raw_fd(raw_fd) }
+        } else {
+            // Open the file, if not exist, create it.
+            std::fs::OpenOptions::new()
+                .read(true)
+                .write(true)
+                .create(true)
+                .open(path)
+                .chain_err(|| "Open file-backend failed")?
+        };
+
+        if file.metadata().unwrap().len() == 0 {
+            file.set_len(file_len)
+                .chain_err(|| "Set file length failed.")?;
+        }
+
+        Ok(FileBackend {
+            file,
+            offset: 0_u64,
+        })
+    }
+}
+
+/// Create HostMemMappings according to address ranges.
 ///
 /// # Arguments
 ///
 /// * `ranges` - The guest address range that will be mapped.
+/// * `file_path` - The path of backend-file.
 /// * `omit_vm_memory` - Dump guest memory in core file or not.
 pub fn create_host_mmaps(
     ranges: &[(u64, u64)],
+    file_path: Option<&str>,
     omit_vm_memory: bool,
 ) -> Result<Vec<Arc<HostMemMapping>>> {
     let mut mappings = Vec::new();
 
-    for range in ranges.iter() {
-        mappings.push(Arc::new(HostMemMapping::new(
-            GuestAddress(range.0),
-            range.1,
-            omit_vm_memory,
-        )?));
+    match file_path {
+        Some(path) => {
+            // create file-backend
+            let file_len = ranges.iter().fold(0, |acc, x| acc + x.1);
+            let mut f_back = FileBackend::new(path, file_len)?;
+            for range in ranges.iter() {
+                mappings.push(Arc::new(HostMemMapping::new(
+                    GuestAddress(range.0),
+                    range.1,
+                    f_back.file.as_raw_fd(),
+                    f_back.offset,
+                    omit_vm_memory,
+                )?));
+
+                f_back.offset += range.1;
+            }
+        }
+        None => {
+            for range in ranges.iter() {
+                mappings.push(Arc::new(HostMemMapping::new(
+                    GuestAddress(range.0),
+                    range.1,
+                    -1,
+                    0,
+                    omit_vm_memory,
+                )?));
+            }
+        }
     }
 
     Ok(mappings)
@@ -59,6 +144,8 @@ impl HostMemMapping {
     ///
     /// * `guest_addr` - The start address im memory.
     /// * `size` - Size of memory that will be mapped.
+    /// * `file_back` - The file's raw fd that backs memory,
+    /// * `file_offset` - Offset in the file that backs memory.
     /// * `omit_vm_memory` - Dump guest memory in core file or not.
     ///
     /// # Errors
@@ -67,16 +154,23 @@ impl HostMemMapping {
     pub fn new(
         guest_addr: GuestAddress,
         size: u64,
+        file_back: RawFd,
+        file_offset: u64,
         omit_vm_memory: bool,
     ) -> Result<HostMemMapping> {
+        let mut flags = libc::MAP_PRIVATE | libc::MAP_NORESERVE;
+        if file_back == -1 {
+            flags |= libc::MAP_ANONYMOUS;
+        }
+
         let host_addr = unsafe {
             let hva = libc::mmap(
                 std::ptr::null_mut() as *mut libc::c_void,
                 size as libc::size_t,
                 libc::PROT_READ | libc::PROT_WRITE,
-                libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_NORESERVE,
-                -1,
-                0,
+                flags,
+                file_back,
+                file_offset as i64,
             );
             if hva == libc::MAP_FAILED {
                 return Err(ErrorKind::Mmap.into());
@@ -146,9 +240,18 @@ mod test {
 
     #[test]
     fn test_ramblock_creation() {
-        let ram1 = HostMemMapping::new(GuestAddress(0), 100u64, false).unwrap();
-        let ram2 = HostMemMapping::new(GuestAddress(0), 100u64, false).unwrap();
+        let ram1 = HostMemMapping::new(GuestAddress(0), 100u64, -1, 0, false).unwrap();
+        let ram2 = HostMemMapping::new(GuestAddress(0), 100u64, -1, 0, false).unwrap();
         identify(ram1, 0, 100);
         identify(ram2, 0, 100);
     }
+
+    #[test]
+    fn test_file_backend() {
+        let file_path = String::from("/tmp/");
+        let file_size = 100u64;
+        let f_back = FileBackend::new(&file_path, file_size);
+        assert!(f_back.is_ok());
+        assert_eq!(f_back.as_ref().unwrap().offset, 0u64);
+    }
 }
diff --git a/address_space/src/lib.rs b/address_space/src/lib.rs
index 98a7ae6..98f4380 100644
--- a/address_space/src/lib.rs
+++ b/address_space/src/lib.rs
@@ -18,7 +18,7 @@
 //! ```rust
 //! use std::sync::{Arc, Mutex};
 //! extern crate address_space;
-//! use address_space::{AddressSpace, Region, GuestAddress, HostMemMapping, RegionOps};
+//! use address_space::{AddressSpace, Region, GuestAddress, HostMemMapping, RegionOps, FileBackend};
 //!
 //! struct DummyDevice;
 //! impl DummyDevice {
@@ -37,7 +37,7 @@
 //!     let space = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap();
 //!
 //!     // 2. create an Ram-type Region, and set it's priority
-//!     let mem_mapping = Arc::new(HostMemMapping::new(GuestAddress(0), 0x1000, false).unwrap());
+//!     let mem_mapping = Arc::new(HostMemMapping::new(GuestAddress(0), 0x1000, -1, 0, false).unwrap());
 //!     let ram_region = Region::init_ram_region(mem_mapping.clone());
 //!     ram_region.set_priority(10);
 //!
@@ -87,7 +87,7 @@ mod region;
 
 pub use address::{AddressRange, GuestAddress};
 pub use address_space::AddressSpace;
-pub use host_mmap::{create_host_mmaps, HostMemMapping};
+pub use host_mmap::{create_host_mmaps, FileBackend, HostMemMapping};
 #[cfg(target_arch = "x86_64")]
 pub use listener::KvmIoListener;
 pub use listener::KvmMemoryListener;
diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs
index 66a3731..c7aca78 100644
--- a/address_space/src/listener.rs
+++ b/address_space/src/listener.rs
@@ -519,7 +519,8 @@ mod test {
     }
 
     fn create_ram_range(addr: u64, size: u64, offset_in_region: u64) -> FlatRange {
-        let mem_mapping = Arc::new(HostMemMapping::new(GuestAddress(addr), size, false).unwrap());
+        let mem_mapping =
+            Arc::new(HostMemMapping::new(GuestAddress(addr), size, -1, 0, false).unwrap());
         FlatRange {
             addr_range: AddressRange::new(
                 mem_mapping.start_address().unchecked_add(offset_in_region),
diff --git a/address_space/src/region.rs b/address_space/src/region.rs
index 45e70c0..236c095 100644
--- a/address_space/src/region.rs
+++ b/address_space/src/region.rs
@@ -677,7 +677,8 @@ mod test {
 
     #[test]
     fn test_ram_region() {
-        let mem_mapping = Arc::new(HostMemMapping::new(GuestAddress(0), 1024u64, false).unwrap());
+        let mem_mapping =
+            Arc::new(HostMemMapping::new(GuestAddress(0), 1024u64, -1, 0, false).unwrap());
         let ram_region = Region::init_ram_region(mem_mapping.clone());
         let data: [u8; 10] = [10; 10];
         let mut res_data: [u8; 10] = [0; 10];
@@ -711,7 +712,7 @@ mod test {
     fn test_ram_region_access() {
         // the target guest address is 0~1024 (1024 not included)
         let rgn_start = GuestAddress(0);
-        let host_mmap = HostMemMapping::new(GuestAddress(0), 1024u64, false).unwrap();
+        let host_mmap = HostMemMapping::new(GuestAddress(0), 1024u64, -1, 0, false).unwrap();
         let ram_region = Region::init_ram_region(Arc::new(host_mmap));
         let mut file = std::fs::File::create("/tmp/test_read_write_buffer.tmp").unwrap();
         let mut file_read = std::fs::File::open("/tmp/test_read_write_buffer.tmp").unwrap();
diff --git a/boot_loader/src/x86_64/bootparam.rs b/boot_loader/src/x86_64/bootparam.rs
index 8023823..34a4f73 100644
--- a/boot_loader/src/x86_64/bootparam.rs
+++ b/boot_loader/src/x86_64/bootparam.rs
@@ -179,7 +179,8 @@ mod test {
         // test setup_boot_params function
         let root = Region::init_container_region(0x2000_0000);
         let space = AddressSpace::new(root.clone()).unwrap();
-        let ram1 = Arc::new(HostMemMapping::new(GuestAddress(0), 0x1000_0000, false).unwrap());
+        let ram1 =
+            Arc::new(HostMemMapping::new(GuestAddress(0), 0x1000_0000, -1, 0, false).unwrap());
         let region_a = Region::init_ram_region(ram1.clone());
         root.add_subregion(region_a, ram1.start_address().raw_value())
             .unwrap();
diff --git a/boot_loader/src/x86_64/mod.rs b/boot_loader/src/x86_64/mod.rs
index 7885b93..9a481bf 100644
--- a/boot_loader/src/x86_64/mod.rs
+++ b/boot_loader/src/x86_64/mod.rs
@@ -499,7 +499,8 @@ mod test {
     fn test_x86_bootloader_and_kernel_cmdline() {
         let root = Region::init_container_region(0x2000_0000);
         let space = AddressSpace::new(root.clone()).unwrap();
-        let ram1 = Arc::new(HostMemMapping::new(GuestAddress(0), 0x1000_0000, false).unwrap());
+        let ram1 =
+            Arc::new(HostMemMapping::new(GuestAddress(0), 0x1000_0000, -1, 0, false).unwrap());
         let region_a = Region::init_ram_region(ram1.clone());
         root.add_subregion(region_a, ram1.start_address().raw_value())
             .unwrap();
diff --git a/device_model/src/micro_vm/mod.rs b/device_model/src/micro_vm/mod.rs
index 0f0cbad..06a320d 100644
--- a/device_model/src/micro_vm/mod.rs
+++ b/device_model/src/micro_vm/mod.rs
@@ -228,7 +228,8 @@ impl LightMachine {
         // Init guest-memory
         // Define ram-region ranges according to architectures
         let ram_ranges = Self::arch_ram_ranges(vm_config.machine_config.mem_size);
-        let mem_mappings = create_host_mmaps(&ram_ranges, vm_config.machine_config.omit_vm_memory)?;
+        let mem_mappings =
+            create_host_mmaps(&ram_ranges, None, vm_config.machine_config.omit_vm_memory)?;
         for mmap in mem_mappings.iter() {
             sys_mem.root().add_subregion(
                 Region::init_ram_region(mmap.clone()),
diff --git a/device_model/src/mmio/virtio_mmio.rs b/device_model/src/mmio/virtio_mmio.rs
index 90877f8..de30cbc 100644
--- a/device_model/src/mmio/virtio_mmio.rs
+++ b/device_model/src/mmio/virtio_mmio.rs
@@ -526,8 +526,9 @@ mod tests {
     fn address_space_init() -> Arc<AddressSpace> {
         let root = Region::init_container_region(1 << 36);
         let sys_space = AddressSpace::new(root).unwrap();
-        let host_mmap =
-            Arc::new(HostMemMapping::new(GuestAddress(0), SYSTEM_SPACE_SIZE, false).unwrap());
+        let host_mmap = Arc::new(
+            HostMemMapping::new(GuestAddress(0), SYSTEM_SPACE_SIZE, -1, 0, false).unwrap(),
+        );
         sys_space
             .root()
             .add_subregion(
diff --git a/device_model/src/virtio/queue.rs b/device_model/src/virtio/queue.rs
index 0efa78a..a6ee9cd 100644
--- a/device_model/src/virtio/queue.rs
+++ b/device_model/src/virtio/queue.rs
@@ -745,8 +745,9 @@ mod tests {
     fn address_space_init() -> Arc<AddressSpace> {
         let root = Region::init_container_region(1 << 36);
         let sys_space = AddressSpace::new(root).unwrap();
-        let host_mmap =
-            Arc::new(HostMemMapping::new(GuestAddress(0), SYSTEM_SPACE_SIZE, false).unwrap());
+        let host_mmap = Arc::new(
+            HostMemMapping::new(GuestAddress(0), SYSTEM_SPACE_SIZE, -1, 0, false).unwrap(),
+        );
         sys_space
             .root()
             .add_subregion(
-- 
2.27.0

