Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .buildkite/pipeline_perf.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@
"tests": "integration_tests/performance/test_jailer.py",
"devtool_opts": "-c 1-10 -m 0",
},
"memory-hotplug": {
"label": ":🔌 Memory hotplug",
"tests": "integration_tests/performance/test_hotplug_memory.py",
"devtool_opts": "-c 1-10 -m 0",
},
}

REVISION_A = os.environ.get("REVISION_A")
Expand Down
16 changes: 16 additions & 0 deletions resources/seccomp/aarch64-unknown-linux-musl.json
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,18 @@
}
]
},
{
"syscall": "ioctl",
"args": [
{
"index": 1,
"type": "dword",
"op": "eq",
"val": 1075883590,
"comment": "KVM_SET_USER_MEMORY_REGION"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can explain why we need it here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack

}
]
},
{
"syscall": "sched_yield",
"comment": "Used by the rust standard library in std::sync::mpmc. Firecracker uses mpsc channels from this module for inter-thread communication"
Expand All @@ -460,6 +472,10 @@
{
"syscall": "restart_syscall",
"comment": "automatically issued by the kernel when specific timing-related syscalls (e.g. nanosleep) get interrupted by SIGSTOP"
},
{
"syscall": "mprotect",
"comment": "Used by memory hotplug to protect access to underlying host memory"
}
]
},
Expand Down
16 changes: 16 additions & 0 deletions resources/seccomp/x86_64-unknown-linux-musl.json
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,18 @@
}
]
},
{
"syscall": "ioctl",
"args": [
{
"index": 1,
"type": "dword",
"op": "eq",
"val": 1075883590,
"comment": "KVM_SET_USER_MEMORY_REGION"
}
]
},
{
"syscall": "sched_yield",
"comment": "Used by the rust standard library in std::sync::mpmc. Firecracker uses mpsc channels from this module for inter-thread communication"
Expand All @@ -472,6 +484,10 @@
{
"syscall": "restart_syscall",
"comment": "automatically issued by the kernel when specific timing-related syscalls (e.g. nanosleep) get interrupted by SIGSTOP"
},
{
"syscall": "mprotect",
"comment": "Used by memory hotplug to protect access to underlying host memory"
}
]
},
Expand Down
5 changes: 4 additions & 1 deletion src/vmm/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,10 @@ pub fn build_microvm_for_boot(
let hotplug_memory_region = vm_resources
.allocate_memory_region(addr, mib_to_bytes(memory_hotplug.total_size_mib))
.map_err(StartMicrovmError::GuestMemory)?;
vm.register_hotpluggable_memory_region(hotplug_memory_region)?;
vm.register_hotpluggable_memory_region(
hotplug_memory_region,
mib_to_bytes(memory_hotplug.slot_size_mib),
)?;
Some(addr)
} else {
None
Expand Down
37 changes: 33 additions & 4 deletions src/vmm/src/devices/virtio/mem/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ use crate::devices::virtio::transport::{VirtioInterrupt, VirtioInterruptType};
use crate::logger::{IncMetric, debug, error};
use crate::utils::{bytes_to_mib, mib_to_bytes, u64_to_usize, usize_to_u64};
use crate::vstate::interrupts::InterruptError;
use crate::vstate::memory::{ByteValued, GuestMemoryExtension, GuestMemoryMmap, GuestRegionMmap};
use crate::vstate::memory::{
ByteValued, GuestMemoryExtension, GuestMemoryMmap, GuestRegionMmap, GuestRegionType,
};
use crate::vstate::vm::VmError;
use crate::{Vm, impl_device_type};

Expand Down Expand Up @@ -76,6 +78,8 @@ pub enum VirtioMemError {
PlugRequestIsTooBig,
/// The requested range cannot be unplugged because it's {0:?}.
UnplugRequestBlockStateInvalid(BlockRangeState),
/// There was an error updating the KVM slot.
UpdateKvmSlot(VmError),
}

#[derive(Debug)]
Expand Down Expand Up @@ -501,6 +505,32 @@ impl VirtioMem {
&self.activate_event
}

fn update_kvm_slots(&self, updated_range: &RequestedRange) -> Result<(), VirtioMemError> {
let hp_region = self
.guest_memory()
.iter()
.find(|r| r.region_type == GuestRegionType::Hotpluggable)
.expect("there should be one and only one hotpluggable region");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how do we verify it's the only one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not verified, if there are multiple it's gonna return the first one. I specified it in the same sentence because I didn't want to leave a separate comment.

hp_region
.slots_intersecting_range(
updated_range.addr,
self.nb_blocks_to_len(updated_range.nb_blocks),
)
.try_for_each(|slot| {
let slot_range = RequestedRange {
addr: slot.guest_addr,
nb_blocks: slot.slice.len() / u64_to_usize(self.config.block_size),
};
match self.range_state(&slot_range) {
BlockRangeState::Mixed | BlockRangeState::Plugged => {
hp_region.update_slot(&self.vm, slot.slot, true)
}
BlockRangeState::Unplugged => hp_region.update_slot(&self.vm, slot.slot, false),
}
.map_err(VirtioMemError::UpdateKvmSlot)
})
}

/// Plugs/unplugs the given range
///
/// Note: the range passed to this function must be within the device memory to avoid
Expand All @@ -527,9 +557,7 @@ impl VirtioMem {
});
}

// TODO: update KVM slots to plug/unplug them

Ok(())
self.update_kvm_slots(range)
}

/// Updates the requested size of the virtio-mem device.
Expand Down Expand Up @@ -692,6 +720,7 @@ pub(crate) mod test_utils {
.unwrap()
.pop()
.unwrap(),
mib_to_bytes(128),
);
let vm = Arc::new(vm);
VirtioMem::new(vm, addr, 1024, 2, 128).unwrap()
Expand Down
16 changes: 6 additions & 10 deletions src/vmm/src/devices/virtio/pmem/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ use crate::logger::{IncMetric, error};
use crate::utils::{align_up, u64_to_usize};
use crate::vmm_config::pmem::PmemConfig;
use crate::vstate::memory::{ByteValued, Bytes, GuestMemoryMmap, GuestMmapRegion};
use crate::vstate::vm::VmError;
use crate::{Vm, impl_device_type};

#[derive(Debug, thiserror::Error, displaydoc::Display)]
pub enum PmemError {
/// Cannot set the memory regions: {0}
SetUserMemoryRegion(kvm_ioctls::Error),
SetUserMemoryRegion(VmError),
/// Unablet to allocate a KVM slot for the device
NoKvmSlotAvailable,
/// Error accessing backing file: {0}
Expand Down Expand Up @@ -221,7 +222,7 @@ impl Pmem {

/// Set user memory region in KVM
pub fn set_mem_region(&mut self, vm: &Vm) -> Result<(), PmemError> {
let next_slot = vm.next_kvm_slot().ok_or(PmemError::NoKvmSlotAvailable)?;
let next_slot = vm.next_kvm_slot(1).ok_or(PmemError::NoKvmSlotAvailable)?;
let memory_region = kvm_userspace_memory_region {
slot: next_slot,
guest_phys_addr: self.config_space.start,
Expand All @@ -233,14 +234,9 @@ impl Pmem {
0
},
};
// SAFETY: The fd is a valid VM file descriptor and all fields in the
// `memory_region` struct are valid.
unsafe {
vm.fd()
.set_user_memory_region(memory_region)
.map_err(PmemError::SetUserMemoryRegion)?;
}
Ok(())

vm.set_user_memory_region(memory_region)
.map_err(PmemError::SetUserMemoryRegion)
}

fn handle_queue(&mut self) -> Result<(), PmemError> {
Expand Down
2 changes: 2 additions & 0 deletions src/vmm/src/persist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@ fn send_uffd_handshake(
mod tests {
use std::os::unix::net::UnixListener;

use bitvec::vec::BitVec;
use vmm_sys_util::tempfile::TempFile;

use super::*;
Expand Down Expand Up @@ -694,6 +695,7 @@ mod tests {
base_address: 0,
size: 0x20000,
region_type: GuestRegionType::Dram,
plugged: BitVec::repeat(true, 1),
}],
};

Expand Down
Loading