From 8ee8739bf022e382ad2c36e95dd181739498c9ff Mon Sep 17 00:00:00 2001 From: Egor Lazarchuk Date: Wed, 13 Dec 2023 16:05:27 +0000 Subject: [PATCH] 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 --- vhost/Cargo.toml | 1 + vhost/src/lib.rs | 6 ++++ vhost/src/vhost_user/frontend.rs | 50 ++++++++++++++++++++++++++++++++ vhost/src/vhost_user/message.rs | 9 ++++++ 4 files changed, 66 insertions(+) diff --git a/vhost/Cargo.toml b/vhost/Cargo.toml index ccbabc6..e369c15 100644 --- a/vhost/Cargo.toml +++ b/vhost/Cargo.toml @@ -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" diff --git a/vhost/src/lib.rs b/vhost/src/lib.rs index 1c82861..3c9fea8 100644 --- a/vhost/src/lib.rs +++ b/vhost/src/lib.rs @@ -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 { diff --git a/vhost/src/vhost_user/frontend.rs b/vhost/src/vhost_user/frontend.rs index 940b090..8f625ea 100644 --- a/vhost/src/vhost_user/frontend.rs +++ b/vhost/src/vhost_user/frontend.rs @@ -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; + + /// 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(err: VhostUserError) -> Result { @@ -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 { + 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::(&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 { diff --git a/vhost/src/vhost_user/message.rs b/vhost/src/vhost_user/message.rs index c493360..cc6b675 100644 --- a/vhost/src/vhost_user/message.rs +++ b/vhost/src/vhost_user/message.rs @@ -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)]