Skip to content

Commit

Permalink
arch: implement SHM region management
Browse files Browse the repository at this point in the history
We have multiple devices (fs and gpu) that can use SHM regions. So far,
we were creating a single SHM region so only one device could make use
of it. In this commit, we implement SHM region management so multiple
devices can request their own regions.

A perhaps controversial decision is to NOT use the GuestMemory
abstraction to manage the SHM regions, and instead operate them naked.
The reason behind this is that GuestMemory is primarily designed with
the intention of self-managing the memory mappings, which is exactly the
opposite of what we need to do with SHM regions.

Also, SHM regions operate in very different ways on Linux/KVM and
macOS/HVF. On the first, we need to create a large mapping, register it
with KVM, and then lay SHM windows on top of it as nested mappings. On
the latter, nested mappings aren't supported, so each SHM window must be
explicitly registered with HVF, so we don't need to create nor register
an initial large mapping.

In addition to this, SHM windows on Linux/KVM operate with offsets on
top of the host address, while macOS/HVF operate with offsets on top of
the guest address.

For the moment, we're hardcoding the SHM region sizes for gpu and fs. A
future commit will extend the API so users can configure those sizes as
desired.

Signed-off-by: Sergio Lopez <[email protected]>
  • Loading branch information
slp committed Aug 9, 2024
1 parent 0c6c660 commit 986f54c
Show file tree
Hide file tree
Showing 11 changed files with 196 additions and 57 deletions.
17 changes: 6 additions & 11 deletions src/arch/src/aarch64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ pub mod macos;
#[cfg(target_os = "macos")]
pub use self::macos::*;

use std::cmp::min;
use std::collections::HashMap;
use std::fmt::Debug;

use self::gic::GICDevice;
use crate::ArchMemoryInfo;
use crate::{round_up, ArchMemoryInfo};
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryMmap};

#[cfg(feature = "efi")]
Expand All @@ -42,35 +41,31 @@ pub enum Error {

/// The start of the memory area reserved for MMIO devices.
pub const MMIO_MEM_START: u64 = layout::MAPPED_IO_START;
/// The size of the MMIO shared memory area used by virtio-fs DAX.
pub const MMIO_SHM_SIZE: u64 = 1 << 33;

pub use self::fdt::DeviceInfoForFDT;
use crate::DeviceType;

/// Returns a Vec of the valid memory addresses for aarch64.
/// See [`layout`](layout) module for a drawing of the specific memory model for this platform.
pub fn arch_memory_regions(size: usize) -> (ArchMemoryInfo, Vec<(GuestAddress, usize)>) {
let dram_size = min(size as u64, layout::DRAM_MEM_MAX_SIZE) as usize;
let page_size: usize = unsafe { libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap() };
let dram_size = round_up(size, page_size);
let ram_last_addr = layout::DRAM_MEM_START + (dram_size as u64);
let shm_start_addr = ((ram_last_addr / 0x4000_0000) + 1) * 0x4000_0000;

let info = ArchMemoryInfo {
ram_last_addr,
shm_start_addr,
shm_size: MMIO_SHM_SIZE,
page_size,
};
let regions = if cfg!(feature = "efi") {
vec![
// Space for loading EDK2 and its variables
(GuestAddress(0u64), 0x800_0000),
(GuestAddress(layout::DRAM_MEM_START), dram_size),
(GuestAddress(shm_start_addr), MMIO_SHM_SIZE as usize),
]
} else {
vec![
(GuestAddress(layout::DRAM_MEM_START), dram_size),
(GuestAddress(shm_start_addr), MMIO_SHM_SIZE as usize),
]
vec![(GuestAddress(layout::DRAM_MEM_START), dram_size)]
};

(info, regions)
Expand Down
17 changes: 14 additions & 3 deletions src/arch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ use std::result;
pub struct ArchMemoryInfo {
pub ram_last_addr: u64,
pub shm_start_addr: u64,
pub shm_size: u64,
pub page_size: usize,
}

pub mod shm;
pub use shm::*;

/// Module for aarch64 related functionality.
#[cfg(target_arch = "aarch64")]
pub mod aarch64;
Expand All @@ -22,7 +25,6 @@ pub mod aarch64;
pub use aarch64::{
arch_memory_regions, configure_system, get_kernel_start, initrd_load_addr,
layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE, layout::IRQ_MAX, Error, MMIO_MEM_START,
MMIO_SHM_SIZE,
};

/// Module for x86_64 related functionality.
Expand All @@ -33,7 +35,7 @@ pub mod x86_64;
pub use crate::x86_64::{
arch_memory_regions, configure_system, get_kernel_start, initrd_load_addr,
layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE, layout::IRQ_MAX, Error, BIOS_SIZE, BIOS_START,
MMIO_MEM_START, MMIO_SHM_SIZE, RESET_VECTOR,
MMIO_MEM_START, RESET_VECTOR,
};

/// Type for returning public functions outcome.
Expand Down Expand Up @@ -66,6 +68,15 @@ pub struct InitrdConfig {
/// Default (smallest) memory page size for the supported architectures.
pub const PAGE_SIZE: usize = 4096;

pub fn round_up(size: usize, align: usize) -> usize {
let page_mask = align - 1;
(size + page_mask) & !page_mask
}
pub fn round_down(size: usize, align: usize) -> usize {
let page_mask = !(align - 1);
size & page_mask
}

impl fmt::Display for DeviceType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self:?}")
Expand Down
43 changes: 43 additions & 0 deletions src/arch/src/shm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use crate::round_up;

use super::ArchMemoryInfo;
#[derive(Debug)]
pub enum Error {
OutOfSpace,
}

#[derive(Clone)]
pub struct ShmRegion {
pub guest_addr: u64,
pub size: usize,
}

pub struct ShmManager {
next_guest_addr: u64,
page_size: usize,
}

impl ShmManager {
pub fn new(info: &ArchMemoryInfo) -> ShmManager {
Self {
next_guest_addr: info.shm_start_addr,
page_size: info.page_size,
}
}

pub fn get_region(&mut self, size: usize) -> Result<ShmRegion, Error> {
let size = round_up(size, self.page_size);

let region = ShmRegion {
guest_addr: self.next_guest_addr,
size,
};

if let Some(addr) = self.next_guest_addr.checked_add(size as u64) {
self.next_guest_addr = addr;
Ok(region)
} else {
Err(Error::OutOfSpace)
}
}
}
21 changes: 11 additions & 10 deletions src/arch/src/x86_64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ pub mod msr;
/// Logic for configuring x86_64 registers.
pub mod regs;

use crate::ArchMemoryInfo;
use crate::InitrdConfig;
use crate::{round_up, ArchMemoryInfo, InitrdConfig};
use arch_gen::x86::bootparam::{boot_params, E820_RAM};
use vm_memory::Bytes;
use vm_memory::{
Expand Down Expand Up @@ -60,8 +59,6 @@ const FIRST_ADDR_PAST_32BITS: u64 = 1 << 32;
const MEM_32BIT_GAP_SIZE: u64 = 768 << 20;
/// The start of the memory area reserved for MMIO devices.
pub const MMIO_MEM_START: u64 = FIRST_ADDR_PAST_32BITS - MEM_32BIT_GAP_SIZE;
/// The size of the MMIO shared memory area used by virtio-fs DAX.
pub const MMIO_SHM_SIZE: u64 = 1 << 33;

/// Returns a Vec of the valid memory addresses.
/// These should be used to configure the GuestMemoryMmap structure for the platform.
Expand All @@ -73,6 +70,9 @@ pub fn arch_memory_regions(
kernel_load_addr: u64,
kernel_size: usize,
) -> (ArchMemoryInfo, Vec<(GuestAddress, usize)>) {
let page_size: usize = unsafe { libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap() };

let size = round_up(size, page_size);
if size < (kernel_load_addr + kernel_size as u64) as usize {
panic!("Kernel doesn't fit in RAM");
}
Expand All @@ -90,7 +90,6 @@ pub fn arch_memory_regions(
vec![
(GuestAddress(0), kernel_load_addr as usize),
(GuestAddress(kernel_load_addr + kernel_size as u64), size),
(GuestAddress(FIRST_ADDR_PAST_32BITS), MMIO_SHM_SIZE as usize),
],
)
}
Expand All @@ -108,15 +107,14 @@ pub fn arch_memory_regions(
(MMIO_MEM_START - (kernel_load_addr + kernel_size as u64)) as usize,
),
(GuestAddress(FIRST_ADDR_PAST_32BITS), remaining),
(GuestAddress(shm_start_addr), MMIO_SHM_SIZE as usize),
],
)
}
};
let info = ArchMemoryInfo {
ram_last_addr,
shm_start_addr,
shm_size: MMIO_SHM_SIZE,
page_size,
};
(info, regions)
}
Expand All @@ -132,6 +130,9 @@ pub fn arch_memory_regions(
kernel_load_addr: u64,
kernel_size: usize,
) -> (ArchMemoryInfo, Vec<(GuestAddress, usize)>) {
let page_size: usize = unsafe { libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap() };

let size = round_up(size, page_size);
if size < (kernel_load_addr + kernel_size as u64) as usize {
panic!("Kernel doesn't fit in RAM");
}
Expand Down Expand Up @@ -170,7 +171,7 @@ pub fn arch_memory_regions(
let info = ArchMemoryInfo {
ram_last_addr,
shm_start_addr,
shm_size: 0,
page_size,
};
(info, regions)
}
Expand Down Expand Up @@ -319,7 +320,7 @@ mod tests {
#[test]
fn regions_lt_4gb() {
let (_info, regions) = arch_memory_regions(1usize << 29, KERNEL_LOAD_ADDR, KERNEL_SIZE);
assert_eq!(3, regions.len());
assert_eq!(2, regions.len());
assert_eq!(GuestAddress(0), regions[0].0);
assert_eq!(KERNEL_LOAD_ADDR as usize, regions[0].1);
assert_eq!(
Expand All @@ -333,7 +334,7 @@ mod tests {
fn regions_gt_4gb() {
let (_info, regions) =
arch_memory_regions((1usize << 32) + 0x8000, KERNEL_LOAD_ADDR, KERNEL_SIZE);
assert_eq!(4, regions.len());
assert_eq!(3, regions.len());
assert_eq!(GuestAddress(0), regions[0].0);
assert_eq!(KERNEL_LOAD_ADDR as usize, regions[0].1);
assert_eq!(
Expand Down
1 change: 1 addition & 0 deletions src/devices/src/virtio/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub enum DeviceState {

#[derive(Clone)]
pub struct VirtioShmRegion {
#[cfg(target_os = "linux")]
pub host_addr: u64,
pub guest_addr: u64,
pub size: usize,
Expand Down
12 changes: 10 additions & 2 deletions src/devices/src/virtio/fs/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,19 @@ impl<F: FileSystem + Sync> Server<F> {
x if x == Opcode::CopyFileRange as u32 => self.copyfilerange(in_header, r, w),
x if (x == Opcode::SetupMapping as u32) && shm_region.is_some() => {
let shm = shm_region.unwrap();
self.setupmapping(in_header, r, w, shm.host_addr, shm.size as u64)
#[cfg(target_os = "linux")]
let shm_base_addr = shm.host_addr;
#[cfg(target_os = "macos")]
let shm_base_addr = shm.guest_addr;
self.setupmapping(in_header, r, w, shm_base_addr, shm.size as u64)
}
x if (x == Opcode::RemoveMapping as u32) && shm_region.is_some() => {
let shm = shm_region.unwrap();
self.removemapping(in_header, r, w, shm.host_addr, shm.size as u64)
#[cfg(target_os = "linux")]
let shm_base_addr = shm.host_addr;
#[cfg(target_os = "macos")]
let shm_base_addr = shm.guest_addr;
self.removemapping(in_header, r, w, shm_base_addr, shm.size as u64)
}
_ => reply_error(
linux_error(io::Error::from_raw_os_error(libc::ENOSYS)),
Expand Down
3 changes: 2 additions & 1 deletion src/devices/src/virtio/gpu/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ use utils::eventfd::EventFd;
use vm_memory::{GuestAddress, GuestMemoryMmap};

use super::super::descriptor_utils::{Reader, Writer};
use super::super::{GpuError, Queue as VirtQueue, VirtioShmRegion, VIRTIO_MMIO_INT_VRING};
use super::super::{GpuError, Queue as VirtQueue, VIRTIO_MMIO_INT_VRING};
use super::protocol::{
virtio_gpu_ctrl_hdr, virtio_gpu_mem_entry, GpuCommand, GpuResponse, VirtioGpuResult,
};
use super::virtio_gpu::VirtioGpu;
use crate::legacy::Gic;
use crate::virtio::gpu::protocol::{VIRTIO_GPU_FLAG_FENCE, VIRTIO_GPU_FLAG_INFO_RING_IDX};
use crate::virtio::gpu::virtio_gpu::VirtioGpuRing;
use crate::virtio::VirtioShmRegion;
use crate::Error as DeviceError;

pub struct Worker {
Expand Down
Loading

0 comments on commit 986f54c

Please sign in to comment.