vmm, ch-remote: Allow VFIO fd substitution at receive-migration

The VmConfig that arrives over the migration stream carries the
source's device paths and stale FDs, none of which are usable on the
destination. A received VFIO device therefore needs fresh descriptors
supplied with the request.

VmReceiveMigrationData gains vfio_fds, pairing each device id with a
cdev FD, and iommufd_fd for the backing iommufd, both arriving over
SCM_RIGHTS. Once the received VmConfig is available, each named
device's path or FD is swapped for the received one and the iommufd is
installed before the VM is built, so the device comes up FD backed.

The request is rejected when a substituted device lacks the iommufd
backend, when an id is unknown or repeated, or when a device names no
replacement in vfio_fds. These checks run against the migrated VmConfig
once it has been received.

ch-remote gains the vfio_fds and iommufd_fd options and forwards the
descriptors over SCM_RIGHTS. The D-Bus transport cannot carry file
descriptors and drops them.

Signed-off-by: Saravanan D <saravanand@crusoe.ai>
This commit is contained in:
Saravanan D
2026-07-04 08:29:46 +00:00
committed by Bo Chen
parent 2214dceb07
commit 59a6563a51
5 changed files with 390 additions and 19 deletions

View File

@@ -531,18 +531,19 @@ fn rest_api_do_command(matches: &ArgMatches, socket: &mut UnixStream) -> ApiResu
.map_err(Error::HttpApiClient)
}
Some("receive-migration") => {
let receive_migration_data = receive_migration_data(
let (receive_migration_data, fds) = receive_migration_data(
matches
.subcommand_matches("receive-migration")
.unwrap()
.get_one::<String>("receive_migration_config")
.unwrap(),
)?;
simple_api_command(
simple_api_command_with_fds(
socket,
"PUT",
"receive-migration",
Some(&receive_migration_data),
&fds,
)
.map_err(Error::HttpApiClient)
}
@@ -750,7 +751,7 @@ fn dbus_api_do_command(matches: &ArgMatches, proxy: &DBusApi1ProxyBlocking<'_>)
proxy.api_vm_send_migration(&send_migration_data)
}
Some("receive-migration") => {
let receive_migration_data = receive_migration_data(
let (receive_migration_data, _fds) = receive_migration_data(
matches
.subcommand_matches("receive-migration")
.unwrap()
@@ -962,10 +963,21 @@ fn coredump_config(destination_url: &str) -> String {
serde_json::to_string(&coredump_config).unwrap()
}
fn receive_migration_data(config: &str) -> Result<String, Error> {
let receive_migration_data =
fn receive_migration_data(config: &str) -> Result<(String, Vec<i32>), Error> {
let mut data =
api::VmReceiveMigrationData::parse(config).map_err(Error::ReceiveMigrationConfig)?;
Ok(serde_json::to_string(&receive_migration_data).unwrap())
// The FDs are passed to the server side process via SCM_RIGHTS, in the
// order vfio_fds then the iommufd FD, matching the server's split.
let mut fds: Vec<i32> = match data.vfio_fds.as_mut() {
Some(vfio_fds) => vfio_fds.iter_mut().filter_map(|v| v.fd.take()).collect(),
None => Vec::new(),
};
if let Some(iommufd_fd) = data.iommufd_fd.take() {
fds.push(iommufd_fd);
}
Ok((serde_json::to_string(&data).unwrap(), fds))
}
fn send_migration_data(config: &str) -> Result<String, Error> {