vmm: allow keeping the socket listener around

This allows accepting multiple connections in the migration receive
path.

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 11:17:20 +01:00
committed by Bo Chen
parent 765311085f
commit 7311211b38
2 changed files with 40 additions and 28 deletions

View File

@@ -2298,9 +2298,10 @@ impl RequestHandler for Vmm {
receive_data_migration.receiver_url
);
let mut listener =
migration_transport::receive_migration_listener(&receive_data_migration.receiver_url)?;
// Accept the connection and get the socket
let mut socket =
migration_transport::receive_migration_socket(&receive_data_migration.receiver_url)?;
let mut socket = listener.accept()?;
event!("vm", "migration-receive-started");

View File

@@ -23,6 +23,31 @@ use vm_migration::{MigratableError, Snapshot};
use crate::{GuestMemoryMmap, VmMigrationConfig};
/// Transport-agnostic listener used to receive connections.
#[derive(Debug)]
pub(crate) enum ReceiveListener {
Tcp(TcpListener),
Unix(UnixListener),
}
impl ReceiveListener {
/// Block until a connection is accepted.
pub(crate) fn accept(&mut self) -> Result<SocketStream, MigratableError> {
match self {
ReceiveListener::Tcp(listener) => listener
.accept()
.map(|(socket, _)| SocketStream::Tcp(socket))
.context("Failed to accept TCP migration connection")
.map_err(MigratableError::MigrateReceive),
ReceiveListener::Unix(listener) => listener
.accept()
.map(|(socket, _)| SocketStream::Unix(socket))
.context("Failed to accept Unix migration connection")
.map_err(MigratableError::MigrateReceive),
}
}
}
/// Transport-agnostic stream used by the migration protocol.
pub(crate) enum SocketStream {
Unix(UnixStream),
@@ -138,35 +163,21 @@ pub(crate) fn send_migration_socket(
}
}
/// Bind and accept a migration connection for the receiver side.
pub(crate) fn receive_migration_socket(
/// Bind a migration listener for the receiver side.
pub(crate) fn receive_migration_listener(
receiver_url: &str,
) -> Result<SocketStream, MigratableError> {
) -> Result<ReceiveListener, MigratableError> {
if let Some(address) = receiver_url.strip_prefix("tcp:") {
let listener = TcpListener::bind(address).map_err(|e| {
MigratableError::MigrateReceive(anyhow!("Error binding to TCP socket: {e}"))
})?;
let (socket, _addr) = listener.accept().map_err(|e| {
MigratableError::MigrateReceive(anyhow!(
"Error accepting connection on TCP socket: {e}"
))
})?;
Ok(SocketStream::Tcp(socket))
TcpListener::bind(address)
.map(ReceiveListener::Tcp)
.context("Error binding to TCP socket")
.map_err(MigratableError::MigrateReceive)
} else {
let path = socket_url_to_path(receiver_url).map_err(MigratableError::MigrateSend)?;
let listener = UnixListener::bind(&path).map_err(|e| {
MigratableError::MigrateReceive(anyhow!("Error binding to UNIX socket: {e}"))
})?;
let (socket, _addr) = listener.accept().map_err(|e| {
MigratableError::MigrateReceive(anyhow!(
"Error accepting connection on UNIX socket: {e}"
))
})?;
Ok(SocketStream::Unix(socket))
let path = socket_url_to_path(receiver_url).map_err(MigratableError::MigrateReceive)?;
UnixListener::bind(&path)
.map(ReceiveListener::Unix)
.context("Error binding to UNIX socket")
.map_err(MigratableError::MigrateReceive)
}
}