vmm: keep direct reference to guest memory around

That way we avoid having to grab a lock when receiving a chunk of memory
over the migration socket. This is a necessary prerequisite for having
multiple memory receiving threads.

On-behalf-of: SAP sebastian.eydam@sap.com
Signed-off-by: Sebastian Eydam <sebastian.eydam@cyberus-technology.de>
This commit is contained in:
Sebastian Eydam
2026-03-10 10:26:59 +01:00
committed by Bo Chen
parent ec42ee8004
commit 765311085f

View File

@@ -36,6 +36,7 @@ use serde::{Deserialize, Serialize};
use signal_hook::iterator::{Handle, Signals};
use thiserror::Error;
use tracer::trace_scoped;
use vm_memory::GuestMemoryAtomic;
use vm_memory::bitmap::AtomicBitmap;
use vm_migration::protocol::*;
use vm_migration::{
@@ -622,8 +623,16 @@ enum ReceiveMigrationState {
/// We received file descriptors for memory. This can only happen on UNIX domain sockets.
MemoryFdsReceived(Vec<(u32, File)>),
/// We received the VM configuration. We keep the memory configuration around to populate guest memory. From this point on, the sender can start sending memory updates.
Configured(Arc<Mutex<MemoryManager>>),
/// We received the VM configuration. We keep a direct reference to the guest memory
/// around to populate it without having to acquire a lock (which we would have to do
/// when accessing the memory through the memory manager).
///
/// We keep the memory manager around to pass it into the next state. From this point
/// on, the sender can start sending memory updates.
Configured(
Arc<Mutex<MemoryManager>>,
GuestMemoryAtomic<GuestMemoryMmap>,
),
/// Memory is populated and we received the state. The VM is ready to go.
StateReceived,
@@ -835,14 +844,19 @@ impl Vmm {
)))
};
let mut configure_vm =
|socket: &mut SocketStream,
memory_files: HashMap<u32, File>|
-> std::result::Result<Arc<Mutex<MemoryManager>>, MigratableError> {
let memory_manager = self.vm_receive_config(req, socket, memory_files)?;
Ok(memory_manager)
};
let mut configure_vm = |socket: &mut SocketStream,
memory_files: HashMap<u32, File>|
-> std::result::Result<
(
Arc<Mutex<MemoryManager>>,
GuestMemoryAtomic<GuestMemoryMmap>,
),
MigratableError,
> {
let memory_manager = self.vm_receive_config(req, socket, memory_files)?;
let guest_memory = memory_manager.lock().unwrap().guest_memory();
Ok((memory_manager, guest_memory))
};
let recv_memory_fd = |socket: &mut SocketStream,
mut memory_files: Vec<(u32, File)>|
@@ -865,23 +879,24 @@ impl Vmm {
},
Started => match req.command() {
Command::MemoryFd => recv_memory_fd(socket, Vec::new()).map(MemoryFdsReceived),
Command::Config => configure_vm(socket, Default::default()).map(Configured),
Command::Config => {
configure_vm(socket, Default::default()).map(|res| Configured(res.0, res.1))
}
_ => invalid_command(),
},
MemoryFdsReceived(memory_files) => match req.command() {
Command::MemoryFd => recv_memory_fd(socket, memory_files).map(MemoryFdsReceived),
Command::Config => {
configure_vm(socket, HashMap::from_iter(memory_files)).map(Configured)
}
Command::Config => configure_vm(socket, HashMap::from_iter(memory_files))
.map(|res| Configured(res.0, res.1)),
_ => invalid_command(),
},
Configured(memory_manager) => match req.command() {
Configured(memory_manager, guest_memory) => match req.command() {
Command::Memory => {
self.vm_receive_memory(req, socket, &mut memory_manager.lock().unwrap())?;
Ok(Configured(memory_manager))
self.vm_receive_memory(req, socket, &guest_memory)?;
Ok(Configured(memory_manager, guest_memory))
}
Command::State => {
self.vm_receive_state(req, socket, memory_manager.clone())?;
self.vm_receive_state(req, socket, memory_manager)?;
Ok(StateReceived)
}
_ => invalid_command(),
@@ -1059,12 +1074,12 @@ impl Vmm {
&mut self,
req: &Request,
socket: &mut SocketStream,
memory_manager: &mut MemoryManager,
guest_mem: &GuestMemoryAtomic<GuestMemoryMmap>,
) -> std::result::Result<(), MigratableError> {
let table = MemoryRangeTable::read_from(socket, req.length())?;
// And then the memory itself
migration_transport::receive_memory_ranges(&memory_manager.guest_memory(), &table, socket)
migration_transport::receive_memory_ranges(guest_mem, &table, socket)
}
/// Performs the initial memory transmission (iteration zero) plus a