Skip to content

Commit c4c6229

Browse files
authored
Merge branch 'main' into tests-netns-reuse
2 parents 17d6001 + 11d8a01 commit c4c6229

File tree

10 files changed

+78
-34
lines changed

10 files changed

+78
-34
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ and this project adheres to
2525

2626
- [#4921](https://github.com/firecracker-microvm/firecracker/pull/4921): Fixed
2727
swagger `CpuConfig` definition to include missing aarch64-specific fields.
28+
- [#4916](https://github.com/firecracker-microvm/firecracker/pull/4916): Fixed
29+
`IovDeque` implementation to work with any host page size. This fixes
30+
virtio-net device on non 4K host kernels.
2831

2932
## [1.10.1]
3033

src/firecracker/src/main.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use seccomp::FilterError;
2020
use seccompiler::BpfThreadMap;
2121
use utils::arg_parser::{ArgParser, Argument};
2222
use utils::validators::validate_instance_id;
23+
use vmm::arch::host_page_size;
2324
use vmm::builder::StartMicrovmError;
2425
use vmm::logger::{
2526
debug, error, info, LoggerConfig, ProcessTimeReporter, StoreMetric, LOGGER, METRICS,
@@ -108,6 +109,10 @@ fn main_exec() -> Result<(), MainError> {
108109
// Initialize the logger.
109110
LOGGER.init().map_err(MainError::SetLogger)?;
110111

112+
// First call to this function updates the value to current
113+
// host page size.
114+
_ = host_page_size();
115+
111116
// We need this so that we can reset terminal to canonical mode if panic occurs.
112117
let stdin = io::stdin();
113118

src/vmm/src/arch/aarch64/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ pub fn initrd_load_addr(
9494
guest_mem: &GuestMemoryMmap,
9595
initrd_size: usize,
9696
) -> Result<u64, ConfigurationError> {
97-
let round_to_pagesize = |size| (size + (super::PAGE_SIZE - 1)) & !(super::PAGE_SIZE - 1);
97+
let round_to_pagesize =
98+
|size| (size + (super::GUEST_PAGE_SIZE - 1)) & !(super::GUEST_PAGE_SIZE - 1);
9899
match GuestAddress(get_fdt_addr(guest_mem)).checked_sub(round_to_pagesize(initrd_size) as u64) {
99100
Some(offset) => {
100101
if guest_mem.address_in_range(offset) {

src/vmm/src/arch/mod.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
use std::fmt;
5+
use std::sync::LazyLock;
56

7+
use log::warn;
68
use serde::{Deserialize, Serialize};
79

810
/// Module for aarch64 related functionality.
@@ -52,8 +54,23 @@ pub struct InitrdConfig {
5254
pub size: usize,
5355
}
5456

55-
/// Default (smallest) memory page size for the supported architectures.
56-
pub const PAGE_SIZE: usize = 4096;
57+
/// Default page size for the guest OS.
58+
pub const GUEST_PAGE_SIZE: usize = 4096;
59+
60+
/// Get the size of the host page size.
61+
pub fn host_page_size() -> usize {
62+
/// Default page size for the host OS.
63+
static PAGE_SIZE: LazyLock<usize> = LazyLock::new(|| {
64+
// # Safety: Value always valid
65+
let r = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
66+
usize::try_from(r).unwrap_or_else(|_| {
67+
warn!("Could not get host page size with sysconf, assuming default 4K host pages");
68+
4096
69+
})
70+
});
71+
72+
*PAGE_SIZE
73+
}
5774

5875
impl fmt::Display for DeviceType {
5976
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

src/vmm/src/arch/x86_64/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ pub fn initrd_load_addr(
9797
return Err(ConfigurationError::InitrdAddress);
9898
}
9999

100-
let align_to_pagesize = |address| address & !(super::PAGE_SIZE - 1);
100+
let align_to_pagesize = |address| address & !(super::GUEST_PAGE_SIZE - 1);
101101
Ok(align_to_pagesize(lowmem_size - initrd_size) as u64)
102102
}
103103

src/vmm/src/builder.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,7 +1305,7 @@ pub(crate) mod tests {
13051305
use crate::vstate::memory::GuestMemory;
13061306
let image = make_test_bin();
13071307

1308-
let mem_size: usize = image.len() * 2 + crate::arch::PAGE_SIZE;
1308+
let mem_size: usize = image.len() * 2 + crate::arch::GUEST_PAGE_SIZE;
13091309

13101310
let tempfile = TempFile::new().unwrap();
13111311
let mut tempfile = tempfile.into_file();
@@ -1344,7 +1344,7 @@ pub(crate) mod tests {
13441344
let tempfile = TempFile::new().unwrap();
13451345
let mut tempfile = tempfile.into_file();
13461346
tempfile.write_all(&image).unwrap();
1347-
let gm = single_region_mem_at(crate::arch::PAGE_SIZE as u64 + 1, image.len() * 2);
1347+
let gm = single_region_mem_at(crate::arch::GUEST_PAGE_SIZE as u64 + 1, image.len() * 2);
13481348

13491349
let res = load_initrd(&gm, &mut tempfile);
13501350
assert!(

src/vmm/src/devices/virtio/iov_deque.rs

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::os::fd::AsRawFd;
66
use libc::{c_int, c_void, iovec, off_t, size_t};
77
use memfd;
88

9-
use crate::arch::PAGE_SIZE;
9+
use crate::arch::host_page_size;
1010

1111
#[derive(Debug, thiserror::Error, displaydoc::Display)]
1212
pub enum IovDequeError {
@@ -77,10 +77,7 @@ pub enum IovDequeError {
7777
// pub iov_len: ::size_t,
7878
// }
7979
// ```
80-
//
81-
// This value must be a multiple of 256 because this is the maximum number of `iovec` can fit into
82-
// 1 memory page: 256 * sizeof(iovec) == 4096 == PAGE_SIZE. IovDeque only operates with `PAGE_SIZE`
83-
// granularity.
80+
8481
#[derive(Debug)]
8582
pub struct IovDeque<const L: u16> {
8683
pub iov: *mut libc::iovec,
@@ -92,18 +89,15 @@ pub struct IovDeque<const L: u16> {
9289
unsafe impl<const L: u16> Send for IovDeque<L> {}
9390

9491
impl<const L: u16> IovDeque<L> {
95-
const BYTES: usize = L as usize * std::mem::size_of::<iovec>();
96-
const _ASSERT: () = assert!(Self::BYTES % PAGE_SIZE == 0);
97-
9892
/// Create a [`memfd`] object that represents a single physical page
99-
fn create_memfd() -> Result<memfd::Memfd, IovDequeError> {
93+
fn create_memfd(pages_bytes: usize) -> Result<memfd::Memfd, IovDequeError> {
10094
// Create a sealable memfd.
10195
let opts = memfd::MemfdOptions::default().allow_sealing(true);
10296
let mfd = opts.create("iov_deque")?;
10397

10498
// Resize to system page size.
10599
mfd.as_file()
106-
.set_len(Self::BYTES.try_into().unwrap())
100+
.set_len(pages_bytes.try_into().unwrap())
107101
.map_err(IovDequeError::MemfdResize)?;
108102

109103
// Add seals to prevent further resizing.
@@ -136,13 +130,13 @@ impl<const L: u16> IovDeque<L> {
136130

137131
/// Allocate memory for our ring buffer
138132
///
139-
/// This will allocate 2 * `Self::BYTES` bytes of virtual memory.
140-
fn allocate_ring_buffer_memory() -> Result<*mut c_void, IovDequeError> {
133+
/// This will allocate 2 * `pages_bytes` bytes of virtual memory.
134+
fn allocate_ring_buffer_memory(pages_bytes: usize) -> Result<*mut c_void, IovDequeError> {
141135
// SAFETY: We are calling the system call with valid arguments
142136
unsafe {
143137
Self::mmap(
144138
std::ptr::null_mut(),
145-
Self::BYTES * 2,
139+
pages_bytes * 2,
146140
libc::PROT_NONE,
147141
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
148142
-1,
@@ -151,18 +145,29 @@ impl<const L: u16> IovDeque<L> {
151145
}
152146
}
153147

148+
/// Calculate a number of bytes in full pages required for
149+
/// the type to operate.
150+
fn pages_bytes() -> usize {
151+
let host_page_size = host_page_size();
152+
let bytes = L as usize * std::mem::size_of::<iovec>();
153+
let num_host_pages = bytes.div_ceil(host_page_size);
154+
num_host_pages * host_page_size
155+
}
156+
154157
/// Create a new [`IovDeque`] that can hold memory described by a single VirtIO queue.
155158
pub fn new() -> Result<Self, IovDequeError> {
156-
let memfd = Self::create_memfd()?;
159+
let pages_bytes = Self::pages_bytes();
160+
161+
let memfd = Self::create_memfd(pages_bytes)?;
157162
let raw_memfd = memfd.as_file().as_raw_fd();
158-
let buffer = Self::allocate_ring_buffer_memory()?;
163+
let buffer = Self::allocate_ring_buffer_memory(pages_bytes)?;
159164

160165
// Map the first page of virtual memory to the physical page described by the memfd object
161166
// SAFETY: We are calling the system call with valid arguments
162167
let _ = unsafe {
163168
Self::mmap(
164169
buffer,
165-
Self::BYTES,
170+
pages_bytes,
166171
libc::PROT_READ | libc::PROT_WRITE,
167172
libc::MAP_SHARED | libc::MAP_FIXED,
168173
raw_memfd,
@@ -173,17 +178,17 @@ impl<const L: u16> IovDeque<L> {
173178
// Map the second page of virtual memory to the physical page described by the memfd object
174179
//
175180
// SAFETY: This is safe because:
176-
// * Both `buffer` and the result of `buffer.add(Self::BYTES)` are within bounds of the
181+
// * Both `buffer` and the result of `buffer.add(pages_bytes)` are within bounds of the
177182
// allocation we got from `Self::allocate_ring_buffer_memory`.
178183
// * The resulting pointer is the beginning of the second page of our allocation, so it
179184
// doesn't wrap around the address space.
180-
let next_page = unsafe { buffer.add(Self::BYTES) };
185+
let next_page = unsafe { buffer.add(pages_bytes) };
181186

182187
// SAFETY: We are calling the system call with valid arguments
183188
let _ = unsafe {
184189
Self::mmap(
185190
next_page,
186-
Self::BYTES,
191+
pages_bytes,
187192
libc::PROT_READ | libc::PROT_WRITE,
188193
libc::MAP_SHARED | libc::MAP_FIXED,
189194
raw_memfd,
@@ -311,9 +316,10 @@ impl<const L: u16> IovDeque<L> {
311316

312317
impl<const L: u16> Drop for IovDeque<L> {
313318
fn drop(&mut self) {
319+
let pages_bytes = Self::pages_bytes();
314320
// SAFETY: We are passing an address that we got from a previous allocation of `2 *
315-
// Self::BYTES` bytes by calling mmap
316-
let _ = unsafe { libc::munmap(self.iov.cast(), Self::BYTES * 2) };
321+
// pages_bytes` by calling mmap
322+
let _ = unsafe { libc::munmap(self.iov.cast(), 2 * pages_bytes) };
317323
}
318324
}
319325

@@ -331,6 +337,18 @@ mod tests {
331337
assert_eq!(deque.len(), 0);
332338
}
333339

340+
#[test]
341+
fn test_new_less_than_page() {
342+
let deque = super::IovDeque::<128>::new().unwrap();
343+
assert_eq!(deque.len(), 0);
344+
}
345+
346+
#[test]
347+
fn test_new_more_than_page() {
348+
let deque = super::IovDeque::<512>::new().unwrap();
349+
assert_eq!(deque.len(), 0);
350+
}
351+
334352
fn make_iovec(id: u16, len: u16) -> iovec {
335353
iovec {
336354
iov_base: id as *mut libc::c_void,

src/vmm/src/devices/virtio/iovec.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -815,13 +815,13 @@ mod verification {
815815
use vm_memory::VolatileSlice;
816816

817817
use super::IoVecBuffer;
818+
use crate::arch::GUEST_PAGE_SIZE;
818819
use crate::devices::virtio::iov_deque::IovDeque;
819820
// Redefine `IoVecBufferMut` and `IovDeque` with specific length. Otherwise
820821
// Rust will not know what to do.
821822
type IoVecBufferMutDefault = super::IoVecBufferMut<FIRECRACKER_MAX_QUEUE_SIZE>;
822823
type IovDequeDefault = IovDeque<FIRECRACKER_MAX_QUEUE_SIZE>;
823824

824-
use crate::arch::PAGE_SIZE;
825825
use crate::devices::virtio::queue::FIRECRACKER_MAX_QUEUE_SIZE;
826826

827827
// Maximum memory size to use for our buffers. For the time being 1KB.
@@ -912,8 +912,8 @@ mod verification {
912912
// SAFETY: safe because the layout has non-zero size
913913
let mem = unsafe {
914914
std::alloc::alloc(std::alloc::Layout::from_size_align_unchecked(
915-
2 * PAGE_SIZE,
916-
PAGE_SIZE,
915+
2 * GUEST_PAGE_SIZE,
916+
GUEST_PAGE_SIZE,
917917
))
918918
};
919919
IovDequeDefault {

src/vmm/src/gdb/target.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use vm_memory::{Bytes, GuestAddress, GuestMemoryError};
3333
use super::arch;
3434
#[cfg(target_arch = "aarch64")]
3535
use crate::arch::aarch64::vcpu::VcpuError as AarchVcpuError;
36-
use crate::arch::PAGE_SIZE;
36+
use crate::arch::GUEST_PAGE_SIZE;
3737
use crate::logger::{error, info};
3838
use crate::utils::u64_to_usize;
3939
use crate::vstate::vcpu::VcpuSendEventError;
@@ -396,7 +396,7 @@ impl MultiThreadBase for FirecrackerTarget {
396396
// Compute the amount space left in the page after the gpa
397397
let read_len = std::cmp::min(
398398
data.len(),
399-
PAGE_SIZE - (u64_to_usize(gpa) & (PAGE_SIZE - 1)),
399+
GUEST_PAGE_SIZE - (u64_to_usize(gpa) & (GUEST_PAGE_SIZE - 1)),
400400
);
401401

402402
vmm.guest_memory()
@@ -430,7 +430,7 @@ impl MultiThreadBase for FirecrackerTarget {
430430
// Compute the amount space left in the page after the gpa
431431
let write_len = std::cmp::min(
432432
data.len(),
433-
PAGE_SIZE - (u64_to_usize(gpa) & (PAGE_SIZE - 1)),
433+
GUEST_PAGE_SIZE - (u64_to_usize(gpa) & (GUEST_PAGE_SIZE - 1)),
434434
);
435435

436436
vmm.guest_memory()

src/vmm/src/vstate/vm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,7 @@ pub(crate) mod tests {
593593
let res = vm.set_kvm_memory_regions(&gm, false);
594594
res.unwrap();
595595

596-
// Trying to set a memory region with a size that is not a multiple of PAGE_SIZE
596+
// Trying to set a memory region with a size that is not a multiple of GUEST_PAGE_SIZE
597597
// will result in error.
598598
let gm = single_region_mem(0x10);
599599
let res = vm.set_kvm_memory_regions(&gm, false);

0 commit comments

Comments
 (0)