Skip to content

Commit 986f54c

Browse files
committed
arch: implement SHM region management
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]>
1 parent 0c6c660 commit 986f54c

File tree

11 files changed

+196
-57
lines changed

11 files changed

+196
-57
lines changed

src/arch/src/aarch64/mod.rs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,11 @@ pub mod macos;
1616
#[cfg(target_os = "macos")]
1717
pub use self::macos::*;
1818

19-
use std::cmp::min;
2019
use std::collections::HashMap;
2120
use std::fmt::Debug;
2221

2322
use self::gic::GICDevice;
24-
use crate::ArchMemoryInfo;
23+
use crate::{round_up, ArchMemoryInfo};
2524
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryMmap};
2625

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

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

4845
pub use self::fdt::DeviceInfoForFDT;
4946
use crate::DeviceType;
5047

5148
/// Returns a Vec of the valid memory addresses for aarch64.
5249
/// See [`layout`](layout) module for a drawing of the specific memory model for this platform.
5350
pub fn arch_memory_regions(size: usize) -> (ArchMemoryInfo, Vec<(GuestAddress, usize)>) {
54-
let dram_size = min(size as u64, layout::DRAM_MEM_MAX_SIZE) as usize;
51+
let page_size: usize = unsafe { libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap() };
52+
let dram_size = round_up(size, page_size);
5553
let ram_last_addr = layout::DRAM_MEM_START + (dram_size as u64);
5654
let shm_start_addr = ((ram_last_addr / 0x4000_0000) + 1) * 0x4000_0000;
55+
5756
let info = ArchMemoryInfo {
5857
ram_last_addr,
5958
shm_start_addr,
60-
shm_size: MMIO_SHM_SIZE,
59+
page_size,
6160
};
6261
let regions = if cfg!(feature = "efi") {
6362
vec![
6463
// Space for loading EDK2 and its variables
6564
(GuestAddress(0u64), 0x800_0000),
6665
(GuestAddress(layout::DRAM_MEM_START), dram_size),
67-
(GuestAddress(shm_start_addr), MMIO_SHM_SIZE as usize),
6866
]
6967
} else {
70-
vec![
71-
(GuestAddress(layout::DRAM_MEM_START), dram_size),
72-
(GuestAddress(shm_start_addr), MMIO_SHM_SIZE as usize),
73-
]
68+
vec![(GuestAddress(layout::DRAM_MEM_START), dram_size)]
7469
};
7570

7671
(info, regions)

src/arch/src/lib.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@ use std::result;
1111
pub struct ArchMemoryInfo {
1212
pub ram_last_addr: u64,
1313
pub shm_start_addr: u64,
14-
pub shm_size: u64,
14+
pub page_size: usize,
1515
}
1616

17+
pub mod shm;
18+
pub use shm::*;
19+
1720
/// Module for aarch64 related functionality.
1821
#[cfg(target_arch = "aarch64")]
1922
pub mod aarch64;
@@ -22,7 +25,6 @@ pub mod aarch64;
2225
pub use aarch64::{
2326
arch_memory_regions, configure_system, get_kernel_start, initrd_load_addr,
2427
layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE, layout::IRQ_MAX, Error, MMIO_MEM_START,
25-
MMIO_SHM_SIZE,
2628
};
2729

2830
/// Module for x86_64 related functionality.
@@ -33,7 +35,7 @@ pub mod x86_64;
3335
pub use crate::x86_64::{
3436
arch_memory_regions, configure_system, get_kernel_start, initrd_load_addr,
3537
layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE, layout::IRQ_MAX, Error, BIOS_SIZE, BIOS_START,
36-
MMIO_MEM_START, MMIO_SHM_SIZE, RESET_VECTOR,
38+
MMIO_MEM_START, RESET_VECTOR,
3739
};
3840

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

71+
pub fn round_up(size: usize, align: usize) -> usize {
72+
let page_mask = align - 1;
73+
(size + page_mask) & !page_mask
74+
}
75+
pub fn round_down(size: usize, align: usize) -> usize {
76+
let page_mask = !(align - 1);
77+
size & page_mask
78+
}
79+
6980
impl fmt::Display for DeviceType {
7081
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
7182
write!(f, "{self:?}")

src/arch/src/shm.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use crate::round_up;
2+
3+
use super::ArchMemoryInfo;
4+
#[derive(Debug)]
5+
pub enum Error {
6+
OutOfSpace,
7+
}
8+
9+
#[derive(Clone)]
10+
pub struct ShmRegion {
11+
pub guest_addr: u64,
12+
pub size: usize,
13+
}
14+
15+
pub struct ShmManager {
16+
next_guest_addr: u64,
17+
page_size: usize,
18+
}
19+
20+
impl ShmManager {
21+
pub fn new(info: &ArchMemoryInfo) -> ShmManager {
22+
Self {
23+
next_guest_addr: info.shm_start_addr,
24+
page_size: info.page_size,
25+
}
26+
}
27+
28+
pub fn get_region(&mut self, size: usize) -> Result<ShmRegion, Error> {
29+
let size = round_up(size, self.page_size);
30+
31+
let region = ShmRegion {
32+
guest_addr: self.next_guest_addr,
33+
size,
34+
};
35+
36+
if let Some(addr) = self.next_guest_addr.checked_add(size as u64) {
37+
self.next_guest_addr = addr;
38+
Ok(region)
39+
} else {
40+
Err(Error::OutOfSpace)
41+
}
42+
}
43+
}

src/arch/src/x86_64/mod.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ pub mod msr;
1717
/// Logic for configuring x86_64 registers.
1818
pub mod regs;
1919

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

6663
/// Returns a Vec of the valid memory addresses.
6764
/// These should be used to configure the GuestMemoryMmap structure for the platform.
@@ -73,6 +70,9 @@ pub fn arch_memory_regions(
7370
kernel_load_addr: u64,
7471
kernel_size: usize,
7572
) -> (ArchMemoryInfo, Vec<(GuestAddress, usize)>) {
73+
let page_size: usize = unsafe { libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap() };
74+
75+
let size = round_up(size, page_size);
7676
if size < (kernel_load_addr + kernel_size as u64) as usize {
7777
panic!("Kernel doesn't fit in RAM");
7878
}
@@ -90,7 +90,6 @@ pub fn arch_memory_regions(
9090
vec![
9191
(GuestAddress(0), kernel_load_addr as usize),
9292
(GuestAddress(kernel_load_addr + kernel_size as u64), size),
93-
(GuestAddress(FIRST_ADDR_PAST_32BITS), MMIO_SHM_SIZE as usize),
9493
],
9594
)
9695
}
@@ -108,15 +107,14 @@ pub fn arch_memory_regions(
108107
(MMIO_MEM_START - (kernel_load_addr + kernel_size as u64)) as usize,
109108
),
110109
(GuestAddress(FIRST_ADDR_PAST_32BITS), remaining),
111-
(GuestAddress(shm_start_addr), MMIO_SHM_SIZE as usize),
112110
],
113111
)
114112
}
115113
};
116114
let info = ArchMemoryInfo {
117115
ram_last_addr,
118116
shm_start_addr,
119-
shm_size: MMIO_SHM_SIZE,
117+
page_size,
120118
};
121119
(info, regions)
122120
}
@@ -132,6 +130,9 @@ pub fn arch_memory_regions(
132130
kernel_load_addr: u64,
133131
kernel_size: usize,
134132
) -> (ArchMemoryInfo, Vec<(GuestAddress, usize)>) {
133+
let page_size: usize = unsafe { libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap() };
134+
135+
let size = round_up(size, page_size);
135136
if size < (kernel_load_addr + kernel_size as u64) as usize {
136137
panic!("Kernel doesn't fit in RAM");
137138
}
@@ -170,7 +171,7 @@ pub fn arch_memory_regions(
170171
let info = ArchMemoryInfo {
171172
ram_last_addr,
172173
shm_start_addr,
173-
shm_size: 0,
174+
page_size,
174175
};
175176
(info, regions)
176177
}
@@ -319,7 +320,7 @@ mod tests {
319320
#[test]
320321
fn regions_lt_4gb() {
321322
let (_info, regions) = arch_memory_regions(1usize << 29, KERNEL_LOAD_ADDR, KERNEL_SIZE);
322-
assert_eq!(3, regions.len());
323+
assert_eq!(2, regions.len());
323324
assert_eq!(GuestAddress(0), regions[0].0);
324325
assert_eq!(KERNEL_LOAD_ADDR as usize, regions[0].1);
325326
assert_eq!(
@@ -333,7 +334,7 @@ mod tests {
333334
fn regions_gt_4gb() {
334335
let (_info, regions) =
335336
arch_memory_regions((1usize << 32) + 0x8000, KERNEL_LOAD_ADDR, KERNEL_SIZE);
336-
assert_eq!(4, regions.len());
337+
assert_eq!(3, regions.len());
337338
assert_eq!(GuestAddress(0), regions[0].0);
338339
assert_eq!(KERNEL_LOAD_ADDR as usize, regions[0].1);
339340
assert_eq!(

src/devices/src/virtio/device.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub enum DeviceState {
2121

2222
#[derive(Clone)]
2323
pub struct VirtioShmRegion {
24+
#[cfg(target_os = "linux")]
2425
pub host_addr: u64,
2526
pub guest_addr: u64,
2627
pub size: usize,

src/devices/src/virtio/fs/server.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,19 @@ impl<F: FileSystem + Sync> Server<F> {
137137
x if x == Opcode::CopyFileRange as u32 => self.copyfilerange(in_header, r, w),
138138
x if (x == Opcode::SetupMapping as u32) && shm_region.is_some() => {
139139
let shm = shm_region.unwrap();
140-
self.setupmapping(in_header, r, w, shm.host_addr, shm.size as u64)
140+
#[cfg(target_os = "linux")]
141+
let shm_base_addr = shm.host_addr;
142+
#[cfg(target_os = "macos")]
143+
let shm_base_addr = shm.guest_addr;
144+
self.setupmapping(in_header, r, w, shm_base_addr, shm.size as u64)
141145
}
142146
x if (x == Opcode::RemoveMapping as u32) && shm_region.is_some() => {
143147
let shm = shm_region.unwrap();
144-
self.removemapping(in_header, r, w, shm.host_addr, shm.size as u64)
148+
#[cfg(target_os = "linux")]
149+
let shm_base_addr = shm.host_addr;
150+
#[cfg(target_os = "macos")]
151+
let shm_base_addr = shm.guest_addr;
152+
self.removemapping(in_header, r, w, shm_base_addr, shm.size as u64)
145153
}
146154
_ => reply_error(
147155
linux_error(io::Error::from_raw_os_error(libc::ENOSYS)),

src/devices/src/virtio/gpu/worker.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ use utils::eventfd::EventFd;
1616
use vm_memory::{GuestAddress, GuestMemoryMmap};
1717

1818
use super::super::descriptor_utils::{Reader, Writer};
19-
use super::super::{GpuError, Queue as VirtQueue, VirtioShmRegion, VIRTIO_MMIO_INT_VRING};
19+
use super::super::{GpuError, Queue as VirtQueue, VIRTIO_MMIO_INT_VRING};
2020
use super::protocol::{
2121
virtio_gpu_ctrl_hdr, virtio_gpu_mem_entry, GpuCommand, GpuResponse, VirtioGpuResult,
2222
};
2323
use super::virtio_gpu::VirtioGpu;
2424
use crate::legacy::Gic;
2525
use crate::virtio::gpu::protocol::{VIRTIO_GPU_FLAG_FENCE, VIRTIO_GPU_FLAG_INFO_RING_IDX};
2626
use crate::virtio::gpu::virtio_gpu::VirtioGpuRing;
27+
use crate::virtio::VirtioShmRegion;
2728
use crate::Error as DeviceError;
2829

2930
pub struct Worker {

0 commit comments

Comments
 (0)