vhost: frontend supports POSTCOPY messages

New feature `postcopy` is introduced. It acts as a gate for a
new functionality.
This feature is not compatible with `xen` feature because `xen`
can map memory regions on demand and this does not work with uffd.

Now frondned supports sending POSTCOPY messages to the backend.

The POSTCOPY messages are only usable when
VHOST_USER_PROTOCOL_F_PAGEFAULT feature is negotiated.

The messages and their descriptions are:
- VHOST_USER_POSTCOPY_ADVISE:
    When the front-end sends this message to the backend,
    the back-end must open a userfaultfd for later use
    and send it's fd to the front-end.

- VHOST_USER_POSTCOPY_LISTEN
    When the back-end receives this message it must ensure
    that shared memory is registered with userfaultfd to
    cause faulting of non-present pages.
    This is always sent sometime after a VHOST_USER_POSTCOPY_ADVISE.

- VHOST_USER_POSTCOPY_END
    When the back-end receives this message it must disable the
    userfaultfd. The reply is an acknowledgement only.

Signed-off-by: Egor Lazarchuk <yegorlz@amazon.co.uk>
This commit is contained in:
Egor Lazarchuk 2023-12-13 16:05:27 +00:00 committed by Erik Schilling
parent b5550a941d
commit 8ee8739bf0
4 changed files with 66 additions and 0 deletions

View file

@ -24,6 +24,7 @@ vhost-user = []
vhost-user-frontend = ["vhost-user"]
vhost-user-backend = ["vhost-user"]
xen = ["vm-memory/xen"]
postcopy = []
[dependencies]
bitflags = "2.4"

View file

@ -51,6 +51,12 @@ pub mod vhost_user;
#[cfg(feature = "vhost-vsock")]
pub mod vsock;
/// Due to the way `xen` handles memory mappings we can not combine it with
/// `postcopy` feature which relies on persistent memory mappings. Thus we
/// disallow enabling both features at the same time.
#[cfg(all(feature = "postcopy", feature = "xen"))]
compile_error!("Both `postcopy` and `xen` features can not be enabled at the same time.");
/// Error codes for vhost operations
#[derive(Debug)]
pub enum Error {

View file

@ -72,6 +72,26 @@ pub trait VhostUserFrontend: VhostBackend {
/// Remove a guest memory mapping from vhost.
fn remove_mem_region(&mut self, region: &VhostUserMemoryRegionInfo) -> Result<()>;
/// Sends VHOST_USER_POSTCOPY_ADVISE msg to the backend
/// initiating the beginning of the postcopy process.
/// Backend will return a userfaultfd.
#[cfg(feature = "postcopy")]
fn postcopy_advise(&mut self) -> Result<File>;
/// Sends VHOST_USER_POSTCOPY_LISTEN msg to the backend
/// telling it to register its memory regions with
/// userfaultfd previously received through the
/// [`VhostUserFrontend::postcopy_advise`] call.
#[cfg(feature = "postcopy")]
fn postcopy_listen(&mut self) -> Result<()>;
/// Sends VHOST_USER_POSTCOPY_END msg to the backend
/// indicating the end of the postcopy process.
/// Backend will destroy the userfaultfd object previously
/// sent by [`VhostUserFrontend::postcopy_advise`].
#[cfg(feature = "postcopy")]
fn postcopy_end(&mut self) -> Result<()>;
}
fn error_code<T>(err: VhostUserError) -> Result<T> {
@ -517,6 +537,36 @@ impl VhostUserFrontend for Frontend {
let hdr = node.send_request_with_body(FrontendReq::REM_MEM_REG, &body, None)?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
#[cfg(feature = "postcopy")]
fn postcopy_advise(&mut self) -> Result<File> {
let mut node = self.node();
node.check_proto_feature(VhostUserProtocolFeatures::PAGEFAULT)?;
let hdr = node.send_request_header(FrontendReq::POSTCOPY_ADVISE, None)?;
let (_, files) = node.recv_reply_with_files::<VhostUserEmpty>(&hdr)?;
match take_single_file(files) {
Some(file) => Ok(file),
None => error_code(VhostUserError::IncorrectFds),
}
}
#[cfg(feature = "postcopy")]
fn postcopy_listen(&mut self) -> Result<()> {
let mut node = self.node();
node.check_proto_feature(VhostUserProtocolFeatures::PAGEFAULT)?;
let hdr = node.send_request_header(FrontendReq::POSTCOPY_LISTEN, None)?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
#[cfg(feature = "postcopy")]
fn postcopy_end(&mut self) -> Result<()> {
let mut node = self.node();
node.check_proto_feature(VhostUserProtocolFeatures::PAGEFAULT)?;
let hdr = node.send_request_header(FrontendReq::POSTCOPY_END, None)?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
}
impl AsRawFd for Frontend {

View file

@ -445,6 +445,15 @@ bitflags! {
}
}
/// An empty message.
#[derive(Copy, Clone, Default)]
pub struct VhostUserEmpty;
// SAFETY: Safe because type is zero size.
unsafe impl ByteValued for VhostUserEmpty {}
impl VhostUserMsgValidator for VhostUserEmpty {}
/// A generic message to encapsulate a 64-bit value.
#[repr(transparent)]
#[derive(Copy, Clone, Default)]