vhost_user: add shared object msg
Add SHARED_OBJECT_* vhost-user request IDs to align with standard. Implement backend functions to send these requests. Add SHARED_OBJECT protocol feature. Add GET_SHARED_OBJECT to the frontend requests (not implemented, this is just to have the placeholder and align with current standard). Signed-off-by: Albert Esteve <aesteve@redhat.com>
This commit is contained in:
parent
9323902c59
commit
76c347c108
4 changed files with 215 additions and 5 deletions
|
|
@ -29,6 +29,7 @@ postcopy = []
|
|||
[dependencies]
|
||||
bitflags = "2.4"
|
||||
libc = "0.2.39"
|
||||
uuid = { version = "1.8.0", features=["v4", "fast-rng", "macro-diagnostics"] }
|
||||
|
||||
vmm-sys-util = "0.12.1"
|
||||
vm-memory = { version = "0.14.0", features=["backend-mmap"] }
|
||||
|
|
|
|||
|
|
@ -129,6 +129,29 @@ impl Backend {
|
|||
}
|
||||
|
||||
impl VhostUserFrontendReqHandler for Backend {
|
||||
/// Forward vhost-user shared-object add request to the frontend.
|
||||
fn shared_object_add(&self, uuid: &VhostUserSharedMsg) -> HandlerResult<u64> {
|
||||
self.send_message(BackendReq::SHARED_OBJECT_ADD, uuid, None)
|
||||
}
|
||||
|
||||
/// Forward vhost-user shared-object remove request to the frontend.
|
||||
fn shared_object_remove(&self, uuid: &VhostUserSharedMsg) -> HandlerResult<u64> {
|
||||
self.send_message(BackendReq::SHARED_OBJECT_REMOVE, uuid, None)
|
||||
}
|
||||
|
||||
/// Forward vhost-user shared-object lookup request to the frontend.
|
||||
fn shared_object_lookup(
|
||||
&self,
|
||||
uuid: &VhostUserSharedMsg,
|
||||
fd: &dyn AsRawFd,
|
||||
) -> HandlerResult<u64> {
|
||||
self.send_message(
|
||||
BackendReq::SHARED_OBJECT_LOOKUP,
|
||||
uuid,
|
||||
Some(&[fd.as_raw_fd()]),
|
||||
)
|
||||
}
|
||||
|
||||
/// Forward vhost-user-fs map file requests to the backend.
|
||||
fn fs_backend_map(&self, fs: &VhostUserFSBackendMsg, fd: &dyn AsRawFd) -> HandlerResult<u64> {
|
||||
self.send_message(BackendReq::FS_MAP, fs, Some(&[fd.as_raw_fd()]))
|
||||
|
|
|
|||
|
|
@ -33,6 +33,25 @@ pub trait VhostUserFrontendReqHandler {
|
|||
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
|
||||
}
|
||||
|
||||
/// Handle shared object add operation
|
||||
fn shared_object_add(&self, _uuid: &VhostUserSharedMsg) -> HandlerResult<u64> {
|
||||
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
|
||||
}
|
||||
|
||||
/// Handle shared object remove operation
|
||||
fn shared_object_remove(&self, _uuid: &VhostUserSharedMsg) -> HandlerResult<u64> {
|
||||
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
|
||||
}
|
||||
|
||||
/// Handle shared object lookup operation
|
||||
fn shared_object_lookup(
|
||||
&self,
|
||||
_uuid: &VhostUserSharedMsg,
|
||||
_fd: &dyn AsRawFd,
|
||||
) -> HandlerResult<u64> {
|
||||
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
|
||||
}
|
||||
|
||||
/// Handle virtio-fs map file requests.
|
||||
fn fs_backend_map(&self, _fs: &VhostUserFSBackendMsg, _fd: &dyn AsRawFd) -> HandlerResult<u64> {
|
||||
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
|
||||
|
|
@ -66,6 +85,25 @@ pub trait VhostUserFrontendReqHandlerMut {
|
|||
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
|
||||
}
|
||||
|
||||
/// Handle shared object add operation
|
||||
fn shared_object_add(&mut self, _uuid: &VhostUserSharedMsg) -> HandlerResult<u64> {
|
||||
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
|
||||
}
|
||||
|
||||
/// Handle shared object remove operation
|
||||
fn shared_object_remove(&mut self, _uuid: &VhostUserSharedMsg) -> HandlerResult<u64> {
|
||||
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
|
||||
}
|
||||
|
||||
/// Handle shared object lookup operation
|
||||
fn shared_object_lookup(
|
||||
&mut self,
|
||||
_uuid: &VhostUserSharedMsg,
|
||||
_fd: &dyn AsRawFd,
|
||||
) -> HandlerResult<u64> {
|
||||
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
|
||||
}
|
||||
|
||||
/// Handle virtio-fs map file requests.
|
||||
fn fs_backend_map(
|
||||
&mut self,
|
||||
|
|
@ -103,6 +141,25 @@ impl<S: VhostUserFrontendReqHandlerMut> VhostUserFrontendReqHandler for Mutex<S>
|
|||
self.lock().unwrap().handle_config_change()
|
||||
}
|
||||
|
||||
/// Handle shared object add operation
|
||||
fn shared_object_add(&self, uuid: &VhostUserSharedMsg) -> HandlerResult<u64> {
|
||||
self.lock().unwrap().shared_object_add(uuid)
|
||||
}
|
||||
|
||||
/// Handle shared object remove operation
|
||||
fn shared_object_remove(&self, uuid: &VhostUserSharedMsg) -> HandlerResult<u64> {
|
||||
self.lock().unwrap().shared_object_remove(uuid)
|
||||
}
|
||||
|
||||
/// Handle shared object lookup operation
|
||||
fn shared_object_lookup(
|
||||
&self,
|
||||
uuid: &VhostUserSharedMsg,
|
||||
fd: &dyn AsRawFd,
|
||||
) -> HandlerResult<u64> {
|
||||
self.lock().unwrap().shared_object_lookup(uuid, fd)
|
||||
}
|
||||
|
||||
fn fs_backend_map(&self, fs: &VhostUserFSBackendMsg, fd: &dyn AsRawFd) -> HandlerResult<u64> {
|
||||
self.lock().unwrap().fs_backend_map(fs, fd)
|
||||
}
|
||||
|
|
@ -230,6 +287,24 @@ impl<S: VhostUserFrontendReqHandler> FrontendReqHandler<S> {
|
|||
.handle_config_change()
|
||||
.map_err(Error::ReqHandlerError)
|
||||
}
|
||||
Ok(BackendReq::SHARED_OBJECT_ADD) => {
|
||||
let msg = self.extract_msg_body::<VhostUserSharedMsg>(&hdr, size, &buf)?;
|
||||
self.backend
|
||||
.shared_object_add(&msg)
|
||||
.map_err(Error::ReqHandlerError)
|
||||
}
|
||||
Ok(BackendReq::SHARED_OBJECT_REMOVE) => {
|
||||
let msg = self.extract_msg_body::<VhostUserSharedMsg>(&hdr, size, &buf)?;
|
||||
self.backend
|
||||
.shared_object_remove(&msg)
|
||||
.map_err(Error::ReqHandlerError)
|
||||
}
|
||||
Ok(BackendReq::SHARED_OBJECT_LOOKUP) => {
|
||||
let msg = self.extract_msg_body::<VhostUserSharedMsg>(&hdr, size, &buf)?;
|
||||
self.backend
|
||||
.shared_object_lookup(&msg, &files.unwrap()[0])
|
||||
.map_err(Error::ReqHandlerError)
|
||||
}
|
||||
Ok(BackendReq::FS_MAP) => {
|
||||
let msg = self.extract_msg_body::<VhostUserFSBackendMsg>(&hdr, size, &buf)?;
|
||||
// check_attached_files() has validated files
|
||||
|
|
@ -293,7 +368,7 @@ impl<S: VhostUserFrontendReqHandler> FrontendReqHandler<S> {
|
|||
files: &Option<Vec<File>>,
|
||||
) -> Result<()> {
|
||||
match hdr.get_code() {
|
||||
Ok(BackendReq::FS_MAP | BackendReq::FS_IO) => {
|
||||
Ok(BackendReq::SHARED_OBJECT_LOOKUP | BackendReq::FS_MAP | BackendReq::FS_IO) => {
|
||||
// Expect a single file is passed.
|
||||
match files {
|
||||
Some(files) if files.len() == 1 => Ok(()),
|
||||
|
|
@ -370,14 +445,46 @@ impl<S: VhostUserFrontendReqHandler> AsRawFd for FrontendReqHandler<S> {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
#[cfg(feature = "vhost-user-backend")]
|
||||
use crate::vhost_user::Backend;
|
||||
#[cfg(feature = "vhost-user-backend")]
|
||||
use std::os::unix::io::FromRawFd;
|
||||
|
||||
struct MockFrontendReqHandler {}
|
||||
struct MockFrontendReqHandler {
|
||||
shared_objects: HashSet<Uuid>,
|
||||
}
|
||||
|
||||
impl MockFrontendReqHandler {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
shared_objects: HashSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VhostUserFrontendReqHandlerMut for MockFrontendReqHandler {
|
||||
fn shared_object_add(&mut self, uuid: &VhostUserSharedMsg) -> HandlerResult<u64> {
|
||||
Ok(!self.shared_objects.insert(uuid.uuid) as u64)
|
||||
}
|
||||
|
||||
fn shared_object_remove(&mut self, uuid: &VhostUserSharedMsg) -> HandlerResult<u64> {
|
||||
Ok(!self.shared_objects.remove(&uuid.uuid) as u64)
|
||||
}
|
||||
|
||||
fn shared_object_lookup(
|
||||
&mut self,
|
||||
uuid: &VhostUserSharedMsg,
|
||||
_fd: &dyn AsRawFd,
|
||||
) -> HandlerResult<u64> {
|
||||
if self.shared_objects.get(&uuid.uuid).is_some() {
|
||||
return Ok(0);
|
||||
}
|
||||
Ok(1)
|
||||
}
|
||||
/// Handle virtio-fs map file requests from the backend.
|
||||
fn fs_backend_map(
|
||||
&mut self,
|
||||
|
|
@ -395,7 +502,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_new_frontend_req_handler() {
|
||||
let backend = Arc::new(Mutex::new(MockFrontendReqHandler {}));
|
||||
let backend = Arc::new(Mutex::new(MockFrontendReqHandler::new()));
|
||||
let mut handler = FrontendReqHandler::new(backend).unwrap();
|
||||
|
||||
assert!(handler.get_tx_raw_fd() >= 0);
|
||||
|
|
@ -411,7 +518,7 @@ mod tests {
|
|||
#[cfg(feature = "vhost-user-backend")]
|
||||
#[test]
|
||||
fn test_frontend_backend_req_handler() {
|
||||
let backend = Arc::new(Mutex::new(MockFrontendReqHandler {}));
|
||||
let backend = Arc::new(Mutex::new(MockFrontendReqHandler::new()));
|
||||
let mut handler = FrontendReqHandler::new(backend).unwrap();
|
||||
|
||||
// SAFETY: Safe because `handler` contains valid fds, and we are
|
||||
|
|
@ -428,6 +535,13 @@ mod tests {
|
|||
let res = handler.handle_request().unwrap();
|
||||
assert_eq!(res, 0);
|
||||
handler.handle_request().unwrap_err();
|
||||
// Testing shared object messages.
|
||||
assert_eq!(handler.handle_request().unwrap(), 0);
|
||||
assert_eq!(handler.handle_request().unwrap(), 1);
|
||||
assert_eq!(handler.handle_request().unwrap(), 0);
|
||||
assert_eq!(handler.handle_request().unwrap(), 1);
|
||||
assert_eq!(handler.handle_request().unwrap(), 0);
|
||||
assert_eq!(handler.handle_request().unwrap(), 1);
|
||||
});
|
||||
|
||||
backend
|
||||
|
|
@ -438,6 +552,23 @@ mod tests {
|
|||
backend
|
||||
.fs_backend_unmap(&VhostUserFSBackendMsg::default())
|
||||
.unwrap();
|
||||
|
||||
let shobj_msg = VhostUserSharedMsg {
|
||||
uuid: Uuid::new_v4(),
|
||||
};
|
||||
assert!(backend.shared_object_add(&shobj_msg).is_ok());
|
||||
assert!(backend.shared_object_add(&shobj_msg).is_ok());
|
||||
assert!(backend.shared_object_lookup(&shobj_msg, &fd).is_ok());
|
||||
assert!(backend
|
||||
.shared_object_lookup(
|
||||
&VhostUserSharedMsg {
|
||||
uuid: Uuid::new_v4(),
|
||||
},
|
||||
&fd,
|
||||
)
|
||||
.is_ok());
|
||||
assert!(backend.shared_object_remove(&shobj_msg).is_ok());
|
||||
assert!(backend.shared_object_remove(&shobj_msg).is_ok());
|
||||
// Ensure that the handler thread did not panic.
|
||||
assert!(frontend_handler.join().is_ok());
|
||||
}
|
||||
|
|
@ -445,7 +576,7 @@ mod tests {
|
|||
#[cfg(feature = "vhost-user-backend")]
|
||||
#[test]
|
||||
fn test_frontend_backend_req_handler_with_ack() {
|
||||
let backend = Arc::new(Mutex::new(MockFrontendReqHandler {}));
|
||||
let backend = Arc::new(Mutex::new(MockFrontendReqHandler::new()));
|
||||
let mut handler = FrontendReqHandler::new(backend).unwrap();
|
||||
handler.set_reply_ack_flag(true);
|
||||
|
||||
|
|
@ -463,6 +594,13 @@ mod tests {
|
|||
let res = handler.handle_request().unwrap();
|
||||
assert_eq!(res, 0);
|
||||
handler.handle_request().unwrap_err();
|
||||
// Testing shared object messages.
|
||||
assert_eq!(handler.handle_request().unwrap(), 0);
|
||||
assert_eq!(handler.handle_request().unwrap(), 1);
|
||||
assert_eq!(handler.handle_request().unwrap(), 0);
|
||||
assert_eq!(handler.handle_request().unwrap(), 1);
|
||||
assert_eq!(handler.handle_request().unwrap(), 0);
|
||||
assert_eq!(handler.handle_request().unwrap(), 1);
|
||||
});
|
||||
|
||||
backend.set_reply_ack_flag(true);
|
||||
|
|
@ -472,6 +610,23 @@ mod tests {
|
|||
backend
|
||||
.fs_backend_unmap(&VhostUserFSBackendMsg::default())
|
||||
.unwrap_err();
|
||||
|
||||
let shobj_msg = VhostUserSharedMsg {
|
||||
uuid: Uuid::new_v4(),
|
||||
};
|
||||
assert!(backend.shared_object_add(&shobj_msg).is_ok());
|
||||
assert!(backend.shared_object_add(&shobj_msg).is_err());
|
||||
assert!(backend.shared_object_lookup(&shobj_msg, &fd).is_ok());
|
||||
assert!(backend
|
||||
.shared_object_lookup(
|
||||
&VhostUserSharedMsg {
|
||||
uuid: Uuid::new_v4(),
|
||||
},
|
||||
&fd,
|
||||
)
|
||||
.is_err());
|
||||
assert!(backend.shared_object_remove(&shobj_msg).is_ok());
|
||||
assert!(backend.shared_object_remove(&shobj_msg).is_err());
|
||||
// Ensure that the handler thread did not panic.
|
||||
assert!(frontend_handler.join().is_ok());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ use std::io;
|
|||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use vm_memory::{mmap::NewBitmap, ByteValued, Error as MmapError, FileOffset, MmapRegion};
|
||||
|
||||
#[cfg(feature = "xen")]
|
||||
|
|
@ -178,6 +180,8 @@ enum_value! {
|
|||
/// Query the backend for its device status as defined in the VIRTIO
|
||||
/// specification.
|
||||
GET_STATUS = 40,
|
||||
/// Retrieve a shared object from the device.
|
||||
GET_SHARED_OBJECT = 41,
|
||||
/// Begin transfer of internal state to/from the backend for migration
|
||||
/// purposes.
|
||||
SET_DEVICE_STATE_FD = 42,
|
||||
|
|
@ -203,6 +207,12 @@ enum_value! {
|
|||
VRING_CALL = 4,
|
||||
/// Indicate that an error occurred on the specific vring.
|
||||
VRING_ERR = 5,
|
||||
/// Add a virtio shared object.
|
||||
SHARED_OBJECT_ADD = 6,
|
||||
/// Remove a virtio shared object.
|
||||
SHARED_OBJECT_REMOVE = 7,
|
||||
/// Lookup for a virtio shared object.
|
||||
SHARED_OBJECT_LOOKUP = 8,
|
||||
|
||||
// Non-standard message types.
|
||||
/// Virtio-fs draft: map file content into the window.
|
||||
|
|
@ -444,6 +454,8 @@ bitflags! {
|
|||
const STATUS = 0x0001_0000;
|
||||
/// Support Xen mmap.
|
||||
const XEN_MMAP = 0x0002_0000;
|
||||
/// Support shared objects.
|
||||
const SHARED_OBJECT = 0x0004_0000;
|
||||
/// Support transferring internal device state.
|
||||
const DEVICE_STATE = 0x0008_0000;
|
||||
}
|
||||
|
|
@ -948,6 +960,25 @@ enum_value! {
|
|||
}
|
||||
}
|
||||
|
||||
/// Contains UUID to interact with associated virtio shared object.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct VhostUserSharedMsg {
|
||||
/// UUID of the shared object
|
||||
pub uuid: Uuid,
|
||||
}
|
||||
|
||||
// SAFETY: Safe because VhostUserSharedMsg is a
|
||||
// fixed-size array internally and there is no
|
||||
// compiler-inserted padding.
|
||||
unsafe impl ByteValued for VhostUserSharedMsg {}
|
||||
|
||||
impl VhostUserMsgValidator for VhostUserSharedMsg {
|
||||
fn is_valid(&self) -> bool {
|
||||
!(self.uuid.is_nil() || self.uuid.is_max())
|
||||
}
|
||||
}
|
||||
|
||||
/// Query/send virtio-fs migration state
|
||||
// Note: this struct is not defined as `packed` in the SPEC and although
|
||||
// it is not necessary, since the struct has no padding, it simplifies
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue