From 4e9bda15f101ae93d188335da18559e16c53e0bb Mon Sep 17 00:00:00 2001 From: German Maglione Date: Mon, 13 Nov 2023 14:07:17 +0100 Subject: [PATCH] vhost-user-backend: Add back-end's internal state API If `VHOST_USER_PROTOCOL_F_DEVICE_STATE` has been negotiated, these methods will be called on receiving the `SET_DEVICE_STATE_FD` and `CHECK_DEVICE_STATE` messages. Signed-off-by: Hanna Czenczek Signed-off-by: German Maglione --- vhost-user-backend/src/backend.rs | 108 +++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/vhost-user-backend/src/backend.rs b/vhost-user-backend/src/backend.rs index 0a9c673..e78dac5 100644 --- a/vhost-user-backend/src/backend.rs +++ b/vhost-user-backend/src/backend.rs @@ -18,11 +18,14 @@ //! [VhostUserBackend]: trait.VhostUserBackend.html //! [VhostUserBackendMut]: trait.VhostUserBackendMut.html +use std::fs::File; use std::io::Result; use std::ops::Deref; use std::sync::{Arc, Mutex, RwLock}; -use vhost::vhost_user::message::VhostUserProtocolFeatures; +use vhost::vhost_user::message::{ + VhostTransferStateDirection, VhostTransferStatePhase, VhostUserProtocolFeatures, +}; use vhost::vhost_user::Backend; use vm_memory::bitmap::Bitmap; use vmm_sys_util::epoll::EventSet; @@ -110,6 +113,37 @@ pub trait VhostUserBackend: Send + Sync { vrings: &[Self::Vring], thread_id: usize, ) -> Result<()>; + + /// Initiate transfer of internal state for the purpose of migration to/from the back-end. + /// + /// Depending on `direction`, the state should either be saved (i.e. serialized and written to + /// `file`) or loaded (i.e. read from `file` and deserialized). The back-end can choose to use + /// a different channel than file. If so, it must return a File that the front-end can use. + /// Note that this function must not block during transfer, i.e. I/O to/from `file` must be + /// done outside of this function. + fn set_device_state_fd( + &self, + _direction: VhostTransferStateDirection, + _phase: VhostTransferStatePhase, + _file: File, + ) -> Result> { + Err(std::io::Error::new( + std::io::ErrorKind::Unsupported, + "back end does not support state transfer", + )) + } + + /// After transferring internal state, check for any resulting errors, including potential + /// deserialization errors when loading state. + /// + /// Although this function return a `Result`, the front-end will not receive any details about + /// this error. + fn check_device_state(&self) -> Result<()> { + Err(std::io::Error::new( + std::io::ErrorKind::Unsupported, + "back end does not support state transfer", + )) + } } /// Trait without interior mutability for vhost user backend servers to implement concrete services. @@ -190,6 +224,35 @@ pub trait VhostUserBackendMut: Send + Sync { vrings: &[Self::Vring], thread_id: usize, ) -> Result<()>; + + /// Initiate transfer of internal state for the purpose of migration to/from the back-end. + /// + /// Depending on `direction`, the state should either be saved (i.e. serialized and written to + /// `file`) or loaded (i.e. read from `file` and deserialized). Note that this function must + /// not block during transfer, i.e. I/O to/from `file` must be done outside of this function. + fn set_device_state_fd( + &mut self, + _direction: VhostTransferStateDirection, + _phase: VhostTransferStatePhase, + _file: File, + ) -> Result> { + Err(std::io::Error::new( + std::io::ErrorKind::Unsupported, + "back end does not support state transfer", + )) + } + + /// After transferring internal state, check for any resulting errors, including potential + /// deserialization errors when loading state. + /// + /// Although this function return a `Result`, the front-end will not receive any details about + /// this error. + fn check_device_state(&self) -> Result<()> { + Err(std::io::Error::new( + std::io::ErrorKind::Unsupported, + "back end does not support state transfer", + )) + } } impl VhostUserBackend for Arc { @@ -254,6 +317,19 @@ impl VhostUserBackend for Arc { self.deref() .handle_event(device_event, evset, vrings, thread_id) } + + fn set_device_state_fd( + &self, + direction: VhostTransferStateDirection, + phase: VhostTransferStatePhase, + file: File, + ) -> Result> { + self.deref().set_device_state_fd(direction, phase, file) + } + + fn check_device_state(&self) -> Result<()> { + self.deref().check_device_state() + } } impl VhostUserBackend for Mutex { @@ -319,6 +395,21 @@ impl VhostUserBackend for Mutex { .unwrap() .handle_event(device_event, evset, vrings, thread_id) } + + fn set_device_state_fd( + &self, + direction: VhostTransferStateDirection, + phase: VhostTransferStatePhase, + file: File, + ) -> Result> { + self.lock() + .unwrap() + .set_device_state_fd(direction, phase, file) + } + + fn check_device_state(&self) -> Result<()> { + self.lock().unwrap().check_device_state() + } } impl VhostUserBackend for RwLock { @@ -384,6 +475,21 @@ impl VhostUserBackend for RwLock { .unwrap() .handle_event(device_event, evset, vrings, thread_id) } + + fn set_device_state_fd( + &self, + direction: VhostTransferStateDirection, + phase: VhostTransferStatePhase, + file: File, + ) -> Result> { + self.write() + .unwrap() + .set_device_state_fd(direction, phase, file) + } + + fn check_device_state(&self) -> Result<()> { + self.read().unwrap().check_device_state() + } } #[cfg(test)]