Skip to content

Commit 4029089

Browse files
vireshkjiangliu
authored andcommitted
vhost: Add support for Xen memory mappings
The vm-memory crate now supports Xen specific memory mappings via a special feature: "xen". Add a corresponding feature for vhost crate and add support for Xen memory regions. Update various dependencies to align to the same version of vm-memory crate. Signed-off-by: Viresh Kumar <[email protected]>
1 parent 3e6ba3c commit 4029089

File tree

7 files changed

+242
-39
lines changed

7 files changed

+242
-39
lines changed

crates/vhost/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,16 @@ vhost-net = ["vhost-kern"]
2323
vhost-user = []
2424
vhost-user-master = ["vhost-user"]
2525
vhost-user-slave = ["vhost-user"]
26+
xen = ["vm-memory/xen"]
2627

2728
[dependencies]
2829
bitflags = "1.0"
2930
libc = "0.2.39"
3031

3132
vmm-sys-util = "0.11.0"
32-
vm-memory = "0.11.0"
33+
vm-memory = "0.12.0"
3334

3435
[dev-dependencies]
3536
tempfile = "3.2.0"
36-
vm-memory = { version = "0.11.0", features=["backend-mmap"] }
37+
vm-memory = { version = "0.12.0", features=["backend-mmap"] }
3738
serial_test = "0.5"

crates/vhost/README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,15 @@ The protocol defines two sides of the communication, master and slave.
3030
Master is the application that shares its virtqueues, slave is the consumer
3131
of the virtqueues. Master and slave can be either a client (i.e. connecting)
3232
or server (listening) in the socket communication.
33+
34+
## Xen support
35+
36+
Supporting Xen requires special handling while mapping the guest memory. The
37+
`vm-memory` crate implements xen memory mapping support via a separate feature
38+
`xen`, and this crate uses the same feature name to enable Xen support.
39+
40+
Also, for xen mappings, the memory regions passed by the frontend contains few
41+
extra fields as described in the vhost-user protocol documentation.
42+
43+
It was decided by the `rust-vmm` maintainers to keep the interface simple and
44+
build the crate for either standard Unix memory mapping or Xen, and not both.

crates/vhost/src/backend.rs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@
1010
//! Common traits and structs for vhost-kern and vhost-user backend drivers.
1111
1212
use std::cell::RefCell;
13+
use std::os::unix::io::AsRawFd;
1314
use std::os::unix::io::RawFd;
1415
use std::sync::RwLock;
1516

17+
use vm_memory::{bitmap::Bitmap, Address, GuestMemoryRegion, GuestRegionMmap};
1618
use vmm_sys_util::eventfd::EventFd;
1719

18-
use super::Result;
20+
use super::vhost_user::message::{VhostUserMemoryRegion, VhostUserSingleMemoryRegion};
21+
use super::{Error, Result};
1922

2023
/// Maximum number of memory regions supported.
2124
pub const VHOST_MAX_MEMORY_REGIONS: usize = 255;
@@ -72,6 +75,70 @@ pub struct VhostUserMemoryRegionInfo {
7275
pub mmap_offset: u64,
7376
/// Optional file descriptor for mmap.
7477
pub mmap_handle: RawFd,
78+
79+
#[cfg(feature = "xen")]
80+
/// Xen specific flags.
81+
pub xen_mmap_flags: u32,
82+
83+
#[cfg(feature = "xen")]
84+
/// Xen specific data.
85+
pub xen_mmap_data: u32,
86+
}
87+
88+
impl VhostUserMemoryRegionInfo {
89+
/// Creates Self from GuestRegionMmap.
90+
pub fn from_guest_region<B: Bitmap>(region: &GuestRegionMmap<B>) -> Result<Self> {
91+
let file_offset = region
92+
.file_offset()
93+
.ok_or(Error::InvalidGuestMemoryRegion)?;
94+
95+
Ok(Self {
96+
guest_phys_addr: region.start_addr().raw_value(),
97+
memory_size: region.len(),
98+
userspace_addr: region.as_ptr() as u64,
99+
mmap_offset: file_offset.start(),
100+
mmap_handle: file_offset.file().as_raw_fd(),
101+
#[cfg(feature = "xen")]
102+
xen_mmap_flags: region.xen_mmap_flags(),
103+
#[cfg(feature = "xen")]
104+
xen_mmap_data: region.xen_mmap_data(),
105+
})
106+
}
107+
108+
/// Creates VhostUserMemoryRegion from Self.
109+
pub fn to_region(&self) -> VhostUserMemoryRegion {
110+
#[cfg(not(feature = "xen"))]
111+
return VhostUserMemoryRegion::new(
112+
self.guest_phys_addr,
113+
self.memory_size,
114+
self.userspace_addr,
115+
self.mmap_offset,
116+
);
117+
118+
#[cfg(feature = "xen")]
119+
VhostUserMemoryRegion::with_xen(
120+
self.guest_phys_addr,
121+
self.memory_size,
122+
self.userspace_addr,
123+
self.mmap_offset,
124+
self.xen_mmap_flags,
125+
self.xen_mmap_data,
126+
)
127+
}
128+
129+
/// Creates VhostUserSingleMemoryRegion from Self.
130+
pub fn to_single_region(&self) -> VhostUserSingleMemoryRegion {
131+
VhostUserSingleMemoryRegion::new(
132+
self.guest_phys_addr,
133+
self.memory_size,
134+
self.userspace_addr,
135+
self.mmap_offset,
136+
#[cfg(feature = "xen")]
137+
self.xen_mmap_flags,
138+
#[cfg(feature = "xen")]
139+
self.xen_mmap_data,
140+
)
141+
}
75142
}
76143

77144
/// Shared memory region data for logging dirty pages
@@ -460,6 +527,11 @@ impl VhostUserMemoryRegionInfo {
460527
userspace_addr,
461528
mmap_offset,
462529
mmap_handle,
530+
531+
#[cfg(feature = "xen")]
532+
xen_mmap_flags: vm_memory::MmapXenFlags::UNIX.bits(),
533+
#[cfg(feature = "xen")]
534+
xen_mmap_data: 0,
463535
}
464536
}
465537
}

crates/vhost/src/vhost_user/master.rs

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -193,13 +193,8 @@ impl VhostBackend for Master {
193193
if region.memory_size == 0 || region.mmap_handle < 0 {
194194
return error_code(VhostUserError::InvalidParam);
195195
}
196-
let reg = VhostUserMemoryRegion {
197-
guest_phys_addr: region.guest_phys_addr,
198-
memory_size: region.memory_size,
199-
user_addr: region.userspace_addr,
200-
mmap_offset: region.mmap_offset,
201-
};
202-
ctx.append(&reg, region.mmap_handle);
196+
197+
ctx.append(&region.to_region(), region.mmap_handle);
203198
}
204199

205200
let mut node = self.node();
@@ -501,12 +496,7 @@ impl VhostUserMaster for Master {
501496
return error_code(VhostUserError::InvalidParam);
502497
}
503498

504-
let body = VhostUserSingleMemoryRegion::new(
505-
region.guest_phys_addr,
506-
region.memory_size,
507-
region.userspace_addr,
508-
region.mmap_offset,
509-
);
499+
let body = region.to_single_region();
510500
let fds = [region.mmap_handle];
511501
let hdr = node.send_request_with_body(MasterReq::ADD_MEM_REG, &body, Some(&fds))?;
512502
node.wait_for_ack(&hdr).map_err(|e| e.into())
@@ -519,12 +509,7 @@ impl VhostUserMaster for Master {
519509
return error_code(VhostUserError::InvalidParam);
520510
}
521511

522-
let body = VhostUserSingleMemoryRegion::new(
523-
region.guest_phys_addr,
524-
region.memory_size,
525-
region.userspace_addr,
526-
region.mmap_offset,
527-
);
512+
let body = region.to_single_region();
528513
let hdr = node.send_request_with_body(MasterReq::REM_MEM_REG, &body, None)?;
529514
node.wait_for_ack(&hdr).map_err(|e| e.into())
530515
}

crates/vhost/src/vhost_user/message.rs

Lines changed: 131 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,15 @@
1010
#![allow(clippy::upper_case_acronyms)]
1111

1212
use std::fmt::Debug;
13+
use std::fs::File;
14+
use std::io;
1315
use std::marker::PhantomData;
1416
use std::ops::Deref;
1517

16-
use vm_memory::ByteValued;
18+
use vm_memory::{mmap::NewBitmap, ByteValued, Error as MmapError, FileOffset, MmapRegion};
19+
20+
#[cfg(feature = "xen")]
21+
use vm_memory::{GuestAddress, MmapRange, MmapXenFlags};
1722

1823
use super::{Error, Result};
1924
use crate::VringConfigData;
@@ -422,6 +427,8 @@ bitflags! {
422427
const CONFIGURE_MEM_SLOTS = 0x0000_8000;
423428
/// Support reporting status.
424429
const STATUS = 0x0001_0000;
430+
/// Support Xen mmap.
431+
const XEN_MMAP = 0x0002_0000;
425432
}
426433
}
427434

@@ -492,8 +499,26 @@ pub struct VhostUserMemoryRegion {
492499
pub user_addr: u64,
493500
/// Offset where region starts in the mapped memory.
494501
pub mmap_offset: u64,
502+
503+
#[cfg(feature = "xen")]
504+
/// Xen specific flags.
505+
pub xen_mmap_flags: u32,
506+
507+
#[cfg(feature = "xen")]
508+
/// Xen specific data.
509+
pub xen_mmap_data: u32,
510+
}
511+
512+
impl VhostUserMemoryRegion {
513+
fn is_valid_common(&self) -> bool {
514+
self.memory_size != 0
515+
&& self.guest_phys_addr.checked_add(self.memory_size).is_some()
516+
&& self.user_addr.checked_add(self.memory_size).is_some()
517+
&& self.mmap_offset.checked_add(self.memory_size).is_some()
518+
}
495519
}
496520

521+
#[cfg(not(feature = "xen"))]
497522
impl VhostUserMemoryRegion {
498523
/// Create a new instance.
499524
pub fn new(guest_phys_addr: u64, memory_size: u64, user_addr: u64, mmap_offset: u64) -> Self {
@@ -504,18 +529,74 @@ impl VhostUserMemoryRegion {
504529
mmap_offset,
505530
}
506531
}
532+
533+
/// Creates mmap region from Self.
534+
pub fn mmap_region<B: NewBitmap>(&self, file: File) -> Result<MmapRegion<B>> {
535+
MmapRegion::<B>::from_file(
536+
FileOffset::new(file, self.mmap_offset),
537+
self.memory_size as usize,
538+
)
539+
.map_err(MmapError::MmapRegion)
540+
.map_err(|e| Error::ReqHandlerError(io::Error::new(io::ErrorKind::Other, e)))
541+
}
542+
543+
fn is_valid(&self) -> bool {
544+
self.is_valid_common()
545+
}
507546
}
508547

509-
impl VhostUserMsgValidator for VhostUserMemoryRegion {
548+
#[cfg(feature = "xen")]
549+
impl VhostUserMemoryRegion {
550+
/// Create a new instance.
551+
pub fn with_xen(
552+
guest_phys_addr: u64,
553+
memory_size: u64,
554+
user_addr: u64,
555+
mmap_offset: u64,
556+
xen_mmap_flags: u32,
557+
xen_mmap_data: u32,
558+
) -> Self {
559+
VhostUserMemoryRegion {
560+
guest_phys_addr,
561+
memory_size,
562+
user_addr,
563+
mmap_offset,
564+
xen_mmap_flags,
565+
xen_mmap_data,
566+
}
567+
}
568+
569+
/// Creates mmap region from Self.
570+
pub fn mmap_region<B: NewBitmap>(&self, file: File) -> Result<MmapRegion<B>> {
571+
let range = MmapRange::new(
572+
self.memory_size as usize,
573+
Some(FileOffset::new(file, self.mmap_offset)),
574+
GuestAddress(self.guest_phys_addr),
575+
self.xen_mmap_flags,
576+
self.xen_mmap_data,
577+
);
578+
579+
MmapRegion::<B>::from_range(range)
580+
.map_err(MmapError::MmapRegion)
581+
.map_err(|e| Error::ReqHandlerError(io::Error::new(io::ErrorKind::Other, e)))
582+
}
583+
510584
fn is_valid(&self) -> bool {
511-
if self.memory_size == 0
512-
|| self.guest_phys_addr.checked_add(self.memory_size).is_none()
513-
|| self.user_addr.checked_add(self.memory_size).is_none()
514-
|| self.mmap_offset.checked_add(self.memory_size).is_none()
515-
{
516-
return false;
585+
if !self.is_valid_common() {
586+
false
587+
} else {
588+
// Only of one of FOREIGN or GRANT should be set.
589+
match MmapXenFlags::from_bits(self.xen_mmap_flags) {
590+
Some(flags) => flags.is_valid(),
591+
None => false,
592+
}
517593
}
518-
true
594+
}
595+
}
596+
597+
impl VhostUserMsgValidator for VhostUserMemoryRegion {
598+
fn is_valid(&self) -> bool {
599+
self.is_valid()
519600
}
520601
}
521602

@@ -541,6 +622,7 @@ impl Deref for VhostUserSingleMemoryRegion {
541622
}
542623
}
543624

625+
#[cfg(not(feature = "xen"))]
544626
impl VhostUserSingleMemoryRegion {
545627
/// Create a new instance.
546628
pub fn new(guest_phys_addr: u64, memory_size: u64, user_addr: u64, mmap_offset: u64) -> Self {
@@ -556,6 +638,31 @@ impl VhostUserSingleMemoryRegion {
556638
}
557639
}
558640

641+
#[cfg(feature = "xen")]
642+
impl VhostUserSingleMemoryRegion {
643+
/// Create a new instance.
644+
pub fn new(
645+
guest_phys_addr: u64,
646+
memory_size: u64,
647+
user_addr: u64,
648+
mmap_offset: u64,
649+
xen_mmap_flags: u32,
650+
xen_mmap_data: u32,
651+
) -> Self {
652+
VhostUserSingleMemoryRegion {
653+
padding: 0,
654+
region: VhostUserMemoryRegion::with_xen(
655+
guest_phys_addr,
656+
memory_size,
657+
user_addr,
658+
mmap_offset,
659+
xen_mmap_flags,
660+
xen_mmap_data,
661+
),
662+
}
663+
}
664+
}
665+
559666
// SAFETY: Safe because all fields of VhostUserSingleMemoryRegion are POD.
560667
unsafe impl ByteValued for VhostUserSingleMemoryRegion {}
561668
impl VhostUserMsgValidator for VhostUserSingleMemoryRegion {}
@@ -1001,6 +1108,20 @@ mod tests {
10011108
use super::*;
10021109
use std::mem;
10031110

1111+
#[cfg(feature = "xen")]
1112+
impl VhostUserMemoryRegion {
1113+
fn new(guest_phys_addr: u64, memory_size: u64, user_addr: u64, mmap_offset: u64) -> Self {
1114+
Self::with_xen(
1115+
guest_phys_addr,
1116+
memory_size,
1117+
user_addr,
1118+
mmap_offset,
1119+
MmapXenFlags::FOREIGN.bits(),
1120+
0,
1121+
)
1122+
}
1123+
}
1124+
10041125
#[test]
10051126
fn check_master_request_code() {
10061127
assert!(!MasterReq::is_valid(MasterReq::NOOP as _));
@@ -1106,12 +1227,7 @@ mod tests {
11061227

11071228
#[test]
11081229
fn check_user_memory_region() {
1109-
let mut msg = VhostUserMemoryRegion {
1110-
guest_phys_addr: 0,
1111-
memory_size: 0x1000,
1112-
user_addr: 0,
1113-
mmap_offset: 0,
1114-
};
1230+
let mut msg = VhostUserMemoryRegion::new(0, 0x1000, 0, 0);
11151231
assert!(msg.is_valid());
11161232
msg.guest_phys_addr = 0xFFFFFFFFFFFFEFFF;
11171233
assert!(msg.is_valid());

0 commit comments

Comments
 (0)