Skip to content

Commit f3eac2f

Browse files
author
Dorinda Bassey
committed
Implement file-backed memory using memfd for vhost-user
Add memfd-backed memory region creation to enable memory sharing with vhost-user backends via FD passing. When vhost-user is enabled, all guest RAM regions are created with memfd backing instead of anonymous mmap. This lays the groundwork for vhost-user device support while maintaining backward compatibility such that the VM boots normally with standard memory when vhost-user is not configured. Signed-off-by: Dorinda Bassey <dbassey@redhat.com>
1 parent ca3349b commit f3eac2f

1 file changed

Lines changed: 145 additions & 5 deletions

File tree

src/vmm/src/builder.rs

Lines changed: 145 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ use vm_memory::mmap::MmapRegion;
9393
#[cfg(not(any(feature = "tee", feature = "aws-nitro")))]
9494
use vm_memory::Address;
9595
use vm_memory::Bytes;
96+
#[cfg(feature = "vhost-user")]
97+
use vm_memory::FileOffset;
9698
#[cfg(not(feature = "aws-nitro"))]
9799
use vm_memory::GuestMemory;
98100
#[cfg(all(target_arch = "x86_64", not(feature = "tee")))]
@@ -1331,9 +1333,81 @@ fn load_payload(
13311333
return Err(StartMicrovmError::MissingKernelConfig);
13321334
};
13331335

1334-
let kernel_region = unsafe {
1335-
MmapRegion::build_raw(kernel_host_addr as *mut u8, kernel_size, 0, 0)
1336-
.map_err(StartMicrovmError::InvalidKernelBundle)?
1336+
#[cfg(feature = "vhost-user")]
1337+
let use_vhost_user = _vm_resources.vhost_user_rng_socket.is_some();
1338+
#[cfg(not(feature = "vhost-user"))]
1339+
let use_vhost_user = false;
1340+
1341+
let kernel_region = if use_vhost_user {
1342+
#[cfg(feature = "vhost-user")]
1343+
{
1344+
debug!(
1345+
"Creating file-backed kernel region for vhost-user (size=0x{:x})",
1346+
kernel_size
1347+
);
1348+
// SAFETY: memfd_create is called with a valid null-terminated C string and valid flags.
1349+
// File descriptor ownership is transferred to File::from_raw_fd below.
1350+
let memfd = unsafe {
1351+
let fd = libc::memfd_create(
1352+
b"kernel\0".as_ptr() as *const libc::c_char,
1353+
libc::MFD_CLOEXEC,
1354+
);
1355+
if fd < 0 {
1356+
error!(
1357+
"Failed to create memfd for kernel: {:?}",
1358+
io::Error::last_os_error()
1359+
);
1360+
return Err(io::Error::last_os_error()).map_err(|e| {
1361+
StartMicrovmError::GuestMemoryMmap(vm_memory::Error::MmapRegion(
1362+
vm_memory::mmap::MmapRegionError::Mmap(e),
1363+
))
1364+
})?;
1365+
}
1366+
if libc::ftruncate(fd, kernel_size as i64) < 0 {
1367+
error!(
1368+
"Failed to ftruncate kernel memfd: {:?}",
1369+
io::Error::last_os_error()
1370+
);
1371+
libc::close(fd);
1372+
return Err(io::Error::last_os_error()).map_err(|e| {
1373+
StartMicrovmError::GuestMemoryMmap(vm_memory::Error::MmapRegion(
1374+
vm_memory::mmap::MmapRegionError::Mmap(e),
1375+
))
1376+
})?;
1377+
}
1378+
debug!("Created kernel memfd with fd={}", fd);
1379+
File::from_raw_fd(fd)
1380+
};
1381+
1382+
let file_offset = FileOffset::new(memfd, 0);
1383+
let region = MmapRegion::from_file(file_offset, kernel_size)
1384+
.map_err(StartMicrovmError::InvalidKernelBundle)?;
1385+
1386+
// SAFETY: kernel_host_addr points to valid kernel data of size kernel_size,
1387+
// provided by the kernel bundle loader.
1388+
let kernel_data = unsafe {
1389+
std::slice::from_raw_parts(kernel_host_addr as *const u8, kernel_size)
1390+
};
1391+
// SAFETY: Both source (kernel_data) and destination (region) are valid for
1392+
// kernel_size bytes. Regions don't overlap as dest is newly allocated memfd-backed
1393+
// memory and source is from kernel bundle.
1394+
unsafe {
1395+
let dest = region.as_ptr() as *mut u8;
1396+
std::ptr::copy_nonoverlapping(kernel_data.as_ptr(), dest, kernel_size);
1397+
}
1398+
debug!("Copied kernel data to file-backed region");
1399+
1400+
region
1401+
}
1402+
#[cfg(not(feature = "vhost-user"))]
1403+
unreachable!()
1404+
} else {
1405+
// SAFETY: kernel_host_addr points to valid kernel data of size kernel_size.
1406+
// The memory region is managed by the kernel bundle and remains valid.
1407+
unsafe {
1408+
MmapRegion::build_raw(kernel_host_addr as *mut u8, kernel_size, 0, 0)
1409+
.map_err(StartMicrovmError::InvalidKernelBundle)?
1410+
}
13371411
};
13381412

13391413
Ok((
@@ -1498,10 +1572,76 @@ pub fn create_guest_memory(
14981572
.map_err(StartMicrovmError::ShmCreate)?;
14991573
}
15001574

1575+
// For vhost-user devices, we need file-backed memory so the backend can mmap it
1576+
#[cfg(feature = "vhost-user")]
1577+
let use_vhost_user = vm_resources.vhost_user_rng_socket.is_some();
1578+
#[cfg(not(feature = "vhost-user"))]
1579+
let use_vhost_user = false;
1580+
1581+
// Add SHM regions before creating guest memory
15011582
arch_mem_regions.extend(shm_manager.regions());
15021583

1503-
let guest_mem = GuestMemoryMmap::from_ranges(&arch_mem_regions)
1504-
.map_err(StartMicrovmError::GuestMemoryMmap)?;
1584+
let guest_mem = if use_vhost_user {
1585+
#[cfg(feature = "vhost-user")]
1586+
{
1587+
debug!(
1588+
"Creating file-backed memory for vhost-user (regions: {})",
1589+
arch_mem_regions.len()
1590+
);
1591+
// Create file-backed memory regions using memfd
1592+
let regions_with_files: Vec<_> = arch_mem_regions
1593+
.iter()
1594+
.map(|(addr, size)| {
1595+
debug!(
1596+
" Creating memfd for region: addr=0x{:x}, size=0x{:x}",
1597+
addr.0, size
1598+
);
1599+
// SAFETY: memfd_create is called with a valid null-terminated C string and valid flags.
1600+
// File descriptor ownership is transferred to File::from_raw_fd below.
1601+
let memfd = unsafe {
1602+
let fd = libc::memfd_create(
1603+
b"guest_mem\0".as_ptr() as *const libc::c_char,
1604+
libc::MFD_CLOEXEC,
1605+
);
1606+
if fd < 0 {
1607+
error!("Failed to create memfd: {:?}", io::Error::last_os_error());
1608+
return Err(io::Error::last_os_error());
1609+
}
1610+
if libc::ftruncate(fd, *size as i64) < 0 {
1611+
error!(
1612+
"Failed to ftruncate memfd: {:?}",
1613+
io::Error::last_os_error()
1614+
);
1615+
libc::close(fd);
1616+
return Err(io::Error::last_os_error());
1617+
}
1618+
debug!(" Created memfd with fd={}", fd);
1619+
File::from_raw_fd(fd)
1620+
};
1621+
1622+
let file_offset = FileOffset::new(memfd, 0);
1623+
Ok((*addr, *size, Some(file_offset)))
1624+
})
1625+
.collect::<Result<Vec<_>, io::Error>>()
1626+
.map_err(|e| {
1627+
StartMicrovmError::GuestMemoryMmap(vm_memory::Error::MmapRegion(
1628+
vm_memory::mmap::MmapRegionError::Mmap(e),
1629+
))
1630+
})?;
1631+
1632+
debug!(
1633+
"Created {} file-backed memory regions",
1634+
regions_with_files.len()
1635+
);
1636+
GuestMemoryMmap::from_ranges_with_files(&regions_with_files)
1637+
.map_err(StartMicrovmError::GuestMemoryMmap)?
1638+
}
1639+
#[cfg(not(feature = "vhost-user"))]
1640+
unreachable!()
1641+
} else {
1642+
GuestMemoryMmap::from_ranges(&arch_mem_regions)
1643+
.map_err(StartMicrovmError::GuestMemoryMmap)?
1644+
};
15051645

15061646
let (guest_mem, entry_addr, initrd_config, cmdline) =
15071647
load_payload(vm_resources, guest_mem, &arch_mem_info, payload)?;

0 commit comments

Comments
 (0)