diff options
Diffstat (limited to 'pkgs/applications/virtualization/cloud-hypervisor/vhost/0002-vhost_user-add-shared-memory-region-support.patch')
-rw-r--r-- | pkgs/applications/virtualization/cloud-hypervisor/vhost/0002-vhost_user-add-shared-memory-region-support.patch | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/pkgs/applications/virtualization/cloud-hypervisor/vhost/0002-vhost_user-add-shared-memory-region-support.patch b/pkgs/applications/virtualization/cloud-hypervisor/vhost/0002-vhost_user-add-shared-memory-region-support.patch new file mode 100644 index 00000000000..e4b7f96c030 --- /dev/null +++ b/pkgs/applications/virtualization/cloud-hypervisor/vhost/0002-vhost_user-add-shared-memory-region-support.patch @@ -0,0 +1,492 @@ +From e1f7130bfbe529a7fda1a598866e0dea41b6ba74 Mon Sep 17 00:00:00 2001 +From: David Stevens <stevensd@chromium.org> +Date: Wed, 15 Jun 2022 16:45:12 +0900 +Subject: [PATCH 2/3] vhost_user: add shared memory region support + +Add support for shared memory regions to vhost-user. This is adding +support for a front-end message to query for necessary shared memory +regions plus back-end message to support mapping/unmapping files from +the shared memory region. + +go/vvu-shared-memory + +BUG=b:201745804 +TEST=compiles + +Change-Id: I35c5d260ee09175b68f6778b81883e0070ee0265 +Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3716344 +Reviewed-by: Keiichi Watanabe <keiichiw@chromium.org> +Commit-Queue: David Stevens <stevensd@chromium.org> +Reviewed-by: Alexandre Courbot <acourbot@chromium.org> +Tested-by: kokoro <noreply+kokoro@google.com> +(cherry-picked from commit f436e2706011fa5f34dc415972434aa3299ebc43) +Signed-off-by: Alyssa Ross <alyssa.ross@unikie.com> +--- + crates/vhost-user-backend/src/handler.rs | 10 +- + crates/vhost/src/vhost_user/dummy_slave.rs | 4 + + crates/vhost/src/vhost_user/master.rs | 25 ++++ + .../src/vhost_user/master_req_handler.rs | 57 ++++++-- + crates/vhost/src/vhost_user/message.rs | 136 +++++++++++++++++- + crates/vhost/src/vhost_user/slave_req.rs | 20 ++- + .../vhost/src/vhost_user/slave_req_handler.rs | 15 ++ + 7 files changed, 247 insertions(+), 20 deletions(-) + +diff --git a/crates/vhost-user-backend/src/handler.rs b/crates/vhost-user-backend/src/handler.rs +index 262bf6c..73402b2 100644 +--- a/crates/vhost-user-backend/src/handler.rs ++++ b/crates/vhost-user-backend/src/handler.rs +@@ -11,9 +11,9 @@ use std::sync::Arc; + use std::thread; + + use vhost::vhost_user::message::{ +- VhostUserConfigFlags, VhostUserMemoryRegion, VhostUserProtocolFeatures, +- VhostUserSingleMemoryRegion, VhostUserVirtioFeatures, VhostUserVringAddrFlags, +- VhostUserVringState, ++ VhostSharedMemoryRegion, VhostUserConfigFlags, VhostUserMemoryRegion, ++ VhostUserProtocolFeatures, VhostUserSingleMemoryRegion, VhostUserVirtioFeatures, ++ VhostUserVringAddrFlags, VhostUserVringState, + }; + use vhost::vhost_user::{ + Error as VhostUserError, Result as VhostUserResult, Slave, VhostUserSlaveReqHandlerMut, +@@ -602,6 +602,10 @@ where + + Ok(()) + } ++ ++ fn get_shared_memory_regions(&mut self) -> VhostUserResult<Vec<VhostSharedMemoryRegion>> { ++ Ok(Vec::new()) ++ } + } + + impl<S, V, B: Bitmap> Drop for VhostUserHandler<S, V, B> { +diff --git a/crates/vhost/src/vhost_user/dummy_slave.rs b/crates/vhost/src/vhost_user/dummy_slave.rs +index ae728a0..00a1ae8 100644 +--- a/crates/vhost/src/vhost_user/dummy_slave.rs ++++ b/crates/vhost/src/vhost_user/dummy_slave.rs +@@ -291,4 +291,8 @@ impl VhostUserSlaveReqHandlerMut for DummySlaveReqHandler { + fn remove_mem_region(&mut self, _region: &VhostUserSingleMemoryRegion) -> Result<()> { + Ok(()) + } ++ ++ fn get_shared_memory_regions(&mut self) -> Result<Vec<VhostSharedMemoryRegion>> { ++ Ok(Vec::new()) ++ } + } +diff --git a/crates/vhost/src/vhost_user/master.rs b/crates/vhost/src/vhost_user/master.rs +index 89aec20..6808cb0 100644 +--- a/crates/vhost/src/vhost_user/master.rs ++++ b/crates/vhost/src/vhost_user/master.rs +@@ -72,6 +72,9 @@ pub trait VhostUserMaster: VhostBackend { + + /// Remove a guest memory mapping from vhost. + fn remove_mem_region(&mut self, region: &VhostUserMemoryRegionInfo) -> Result<()>; ++ ++ /// Gets the shared memory regions used by the device. ++ fn get_shared_memory_regions(&self) -> Result<Vec<VhostSharedMemoryRegion>>; + } + + fn error_code<T>(err: VhostUserError) -> Result<T> { +@@ -513,6 +516,28 @@ impl VhostUserMaster for Master { + let hdr = node.send_request_with_body(MasterReq::REM_MEM_REG, &body, None)?; + node.wait_for_ack(&hdr).map_err(|e| e.into()) + } ++ ++ fn get_shared_memory_regions(&self) -> Result<Vec<VhostSharedMemoryRegion>> { ++ let mut node = self.node(); ++ let hdr = node.send_request_header(MasterReq::GET_SHARED_MEMORY_REGIONS, None)?; ++ let (body_reply, buf_reply, rfds) = node.recv_reply_with_payload::<VhostUserU64>(&hdr)?; ++ let struct_size = mem::size_of::<VhostSharedMemoryRegion>(); ++ if rfds.is_some() || buf_reply.len() != body_reply.value as usize * struct_size { ++ return error_code(VhostUserError::InvalidMessage); ++ } ++ let mut regions = Vec::new(); ++ let mut offset = 0; ++ for _ in 0..body_reply.value { ++ regions.push( ++ // Can't fail because the input is the correct size. ++ VhostSharedMemoryRegion::from_slice(&buf_reply[offset..(offset + struct_size)]) ++ .unwrap() ++ .clone(), ++ ); ++ offset += struct_size; ++ } ++ Ok(regions) ++ } + } + + impl AsRawFd for Master { +diff --git a/crates/vhost/src/vhost_user/master_req_handler.rs b/crates/vhost/src/vhost_user/master_req_handler.rs +index c9c528b..3d01542 100644 +--- a/crates/vhost/src/vhost_user/master_req_handler.rs ++++ b/crates/vhost/src/vhost_user/master_req_handler.rs +@@ -33,6 +33,16 @@ pub trait VhostUserMasterReqHandler { + Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) + } + ++ /// Handle shared memory region mapping requests. ++ fn shmem_map(&self, _req: &VhostUserShmemMapMsg, _fd: &dyn AsRawFd) -> HandlerResult<u64> { ++ Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) ++ } ++ ++ /// Handle shared memory region unmapping requests. ++ fn shmem_unmap(&self, _req: &VhostUserShmemUnmapMsg) -> HandlerResult<u64> { ++ Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) ++ } ++ + /// Handle virtio-fs map file requests. + fn fs_slave_map(&self, _fs: &VhostUserFSSlaveMsg, _fd: &dyn AsRawFd) -> HandlerResult<u64> { + Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) +@@ -66,6 +76,16 @@ pub trait VhostUserMasterReqHandlerMut { + Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) + } + ++ /// Handle shared memory region mapping requests. ++ fn shmem_map(&mut self, _req: &VhostUserShmemMapMsg, _fd: &dyn AsRawFd) -> HandlerResult<u64> { ++ Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) ++ } ++ ++ /// Handle shared memory region unmapping requests. ++ fn shmem_unmap(&mut self, _req: &VhostUserShmemUnmapMsg) -> HandlerResult<u64> { ++ Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) ++ } ++ + /// Handle virtio-fs map file requests. + fn fs_slave_map(&mut self, _fs: &VhostUserFSSlaveMsg, _fd: &dyn AsRawFd) -> HandlerResult<u64> { + Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) +@@ -95,6 +115,14 @@ impl<S: VhostUserMasterReqHandlerMut> VhostUserMasterReqHandler for Mutex<S> { + self.lock().unwrap().handle_config_change() + } + ++ fn shmem_map(&self, req: &VhostUserShmemMapMsg, fd: &dyn AsRawFd) -> HandlerResult<u64> { ++ self.lock().unwrap().shmem_map(req, fd) ++ } ++ ++ fn shmem_unmap(&self, req: &VhostUserShmemUnmapMsg) -> HandlerResult<u64> { ++ self.lock().unwrap().shmem_unmap(req) ++ } ++ + fn fs_slave_map(&self, fs: &VhostUserFSSlaveMsg, fd: &dyn AsRawFd) -> HandlerResult<u64> { + self.lock().unwrap().fs_slave_map(fs, fd) + } +@@ -222,6 +250,19 @@ impl<S: VhostUserMasterReqHandler> MasterReqHandler<S> { + .handle_config_change() + .map_err(Error::ReqHandlerError) + } ++ Ok(SlaveReq::SHMEM_MAP) => { ++ let msg = self.extract_msg_body::<VhostUserShmemMapMsg>(&hdr, size, &buf)?; ++ // check_attached_files() has validated files ++ self.backend ++ .shmem_map(&msg, &files.unwrap()[0]) ++ .map_err(Error::ReqHandlerError) ++ } ++ Ok(SlaveReq::SHMEM_UNMAP) => { ++ let msg = self.extract_msg_body::<VhostUserShmemUnmapMsg>(&hdr, size, &buf)?; ++ self.backend ++ .shmem_unmap(&msg) ++ .map_err(Error::ReqHandlerError) ++ } + Ok(SlaveReq::FS_MAP) => { + let msg = self.extract_msg_body::<VhostUserFSSlaveMsg>(&hdr, size, &buf)?; + // check_attached_files() has validated files +@@ -251,7 +292,7 @@ impl<S: VhostUserMasterReqHandler> MasterReqHandler<S> { + _ => Err(Error::InvalidMessage), + }; + +- self.send_ack_message(&hdr, &res)?; ++ self.send_reply(&hdr, &res)?; + + res + } +@@ -285,7 +326,7 @@ impl<S: VhostUserMasterReqHandler> MasterReqHandler<S> { + files: &Option<Vec<File>>, + ) -> Result<()> { + match hdr.get_code() { +- Ok(SlaveReq::FS_MAP | SlaveReq::FS_IO) => { ++ Ok(SlaveReq::SHMEM_MAP | SlaveReq::FS_MAP | SlaveReq::FS_IO) => { + // Expect a single file is passed. + match files { + Some(files) if files.len() == 1 => Ok(()), +@@ -327,12 +368,12 @@ impl<S: VhostUserMasterReqHandler> MasterReqHandler<S> { + )) + } + +- fn send_ack_message( +- &mut self, +- req: &VhostUserMsgHeader<SlaveReq>, +- res: &Result<u64>, +- ) -> Result<()> { +- if self.reply_ack_negotiated && req.is_need_reply() { ++ fn send_reply(&mut self, req: &VhostUserMsgHeader<SlaveReq>, res: &Result<u64>) -> Result<()> { ++ if matches!( ++ req.get_code(), ++ Ok(SlaveReq::SHMEM_MAP | SlaveReq::SHMEM_UNMAP) ++ ) || (self.reply_ack_negotiated && req.is_need_reply()) ++ { + let hdr = self.new_reply_header::<VhostUserU64>(req)?; + let def_err = libc::EINVAL; + let val = match res { +diff --git a/crates/vhost/src/vhost_user/message.rs b/crates/vhost/src/vhost_user/message.rs +index bbd8eb9..be24992 100644 +--- a/crates/vhost/src/vhost_user/message.rs ++++ b/crates/vhost/src/vhost_user/message.rs +@@ -146,8 +146,10 @@ pub enum MasterReq { + /// Query the backend for its device status as defined in the VIRTIO + /// specification. + GET_STATUS = 40, ++ /// Get a list of the device's shared memory regions. ++ GET_SHARED_MEMORY_REGIONS = 41, + /// Upper bound of valid commands. +- MAX_CMD = 41, ++ MAX_CMD = 42, + } + + impl From<MasterReq> for u32 { +@@ -178,16 +180,20 @@ pub enum SlaveReq { + VRING_CALL = 4, + /// Indicate that an error occurred on the specific vring. + VRING_ERR = 5, ++ /// Indicates a request to map a fd into a shared memory region. ++ SHMEM_MAP = 6, ++ /// Indicates a request to unmap part of a shared memory region. ++ SHMEM_UNMAP = 7, + /// Virtio-fs draft: map file content into the window. +- FS_MAP = 6, ++ FS_MAP = 8, + /// Virtio-fs draft: unmap file content from the window. +- FS_UNMAP = 7, ++ FS_UNMAP = 9, + /// Virtio-fs draft: sync file content. +- FS_SYNC = 8, ++ FS_SYNC = 10, + /// Virtio-fs draft: perform a read/write from an fd directly to GPA. +- FS_IO = 9, ++ FS_IO = 11, + /// Upper bound of valid commands. +- MAX_CMD = 10, ++ MAX_CMD = 12, + } + + impl From<SlaveReq> for u32 { +@@ -972,6 +978,99 @@ impl VhostUserMsgValidator for VhostUserFSSlaveMsg { + } + } + ++bitflags! { ++ #[derive(Default)] ++ /// Flags for SHMEM_MAP messages. ++ pub struct VhostUserShmemMapMsgFlags: u8 { ++ /// Empty permission. ++ const EMPTY = 0x0; ++ /// Read permission. ++ const MAP_R = 0x1; ++ /// Write permission. ++ const MAP_W = 0x2; ++ } ++} ++ ++/// Slave request message to map a file into a shared memory region. ++#[repr(C, packed)] ++#[derive(Default, Copy, Clone)] ++pub struct VhostUserShmemMapMsg { ++ /// Flags for the mmap operation ++ pub flags: VhostUserShmemMapMsgFlags, ++ /// Shared memory region id. ++ pub shmid: u8, ++ padding: [u8; 6], ++ /// Offset into the shared memory region. ++ pub shm_offset: u64, ++ /// File offset. ++ pub fd_offset: u64, ++ /// Size of region to map. ++ pub len: u64, ++} ++// Safe because it only has data and has no implicit padding. ++unsafe impl ByteValued for VhostUserShmemMapMsg {} ++ ++impl VhostUserMsgValidator for VhostUserShmemMapMsg { ++ fn is_valid(&self) -> bool { ++ (self.flags.bits() & !VhostUserFSSlaveMsgFlags::all().bits() as u8) == 0 ++ && self.fd_offset.checked_add(self.len).is_some() ++ && self.shm_offset.checked_add(self.len).is_some() ++ } ++} ++ ++impl VhostUserShmemMapMsg { ++ /// New instance of VhostUserShmemMapMsg struct ++ pub fn new( ++ shmid: u8, ++ shm_offset: u64, ++ fd_offset: u64, ++ len: u64, ++ flags: VhostUserShmemMapMsgFlags, ++ ) -> Self { ++ Self { ++ flags, ++ shmid, ++ padding: [0; 6], ++ shm_offset, ++ fd_offset, ++ len, ++ } ++ } ++} ++ ++/// Slave request message to unmap part of a shared memory region. ++#[repr(C, packed)] ++#[derive(Default, Copy, Clone)] ++pub struct VhostUserShmemUnmapMsg { ++ /// Shared memory region id. ++ pub shmid: u8, ++ padding: [u8; 7], ++ /// Offset into the shared memory region. ++ pub shm_offset: u64, ++ /// Size of region to unmap. ++ pub len: u64, ++} ++// Safe because it only has data and has no implicit padding. ++unsafe impl ByteValued for VhostUserShmemUnmapMsg {} ++ ++impl VhostUserMsgValidator for VhostUserShmemUnmapMsg { ++ fn is_valid(&self) -> bool { ++ self.shm_offset.checked_add(self.len).is_some() ++ } ++} ++ ++impl VhostUserShmemUnmapMsg { ++ /// New instance of VhostUserShmemUnmapMsg struct ++ pub fn new(shmid: u8, shm_offset: u64, len: u64) -> Self { ++ Self { ++ shmid, ++ padding: [0; 7], ++ shm_offset, ++ len, ++ } ++ } ++} ++ + /// Inflight I/O descriptor state for split virtqueues + #[repr(packed)] + #[derive(Clone, Copy, Default)] +@@ -1103,6 +1202,31 @@ impl QueueRegionPacked { + } + } + ++/// Virtio shared memory descriptor. ++#[repr(packed)] ++#[derive(Default, Copy, Clone)] ++pub struct VhostSharedMemoryRegion { ++ /// The shared memory region's shmid. ++ pub id: u8, ++ /// Padding ++ padding: [u8; 7], ++ /// The length of the shared memory region. ++ pub length: u64, ++} ++// Safe because it only has data and has no implicit padding. ++unsafe impl ByteValued for VhostSharedMemoryRegion {} ++ ++impl VhostSharedMemoryRegion { ++ /// New instance of VhostSharedMemoryRegion struct ++ pub fn new(id: u8, length: u64) -> Self { ++ VhostSharedMemoryRegion { ++ id, ++ padding: [0; 7], ++ length, ++ } ++ } ++} ++ + #[cfg(test)] + mod tests { + use super::*; +diff --git a/crates/vhost/src/vhost_user/slave_req.rs b/crates/vhost/src/vhost_user/slave_req.rs +index ade1e91..b7ecd20 100644 +--- a/crates/vhost/src/vhost_user/slave_req.rs ++++ b/crates/vhost/src/vhost_user/slave_req.rs +@@ -46,12 +46,16 @@ impl SlaveInternal { + } + self.sock.send_message(&hdr, body, fds)?; + +- self.wait_for_ack(&hdr) ++ self.wait_for_reply(&hdr) + } + +- fn wait_for_ack(&mut self, hdr: &VhostUserMsgHeader<SlaveReq>) -> Result<u64> { ++ fn wait_for_reply(&mut self, hdr: &VhostUserMsgHeader<SlaveReq>) -> Result<u64> { + self.check_state()?; +- if !self.reply_ack_negotiated { ++ if !matches!( ++ hdr.get_code(), ++ Ok(SlaveReq::SHMEM_MAP | SlaveReq::SHMEM_UNMAP) ++ ) && !self.reply_ack_negotiated ++ { + return Ok(0); + } + +@@ -129,6 +133,16 @@ impl Slave { + } + + impl VhostUserMasterReqHandler for Slave { ++ /// Handle shared memory region mapping requests. ++ fn shmem_map(&self, req: &VhostUserShmemMapMsg, fd: &dyn AsRawFd) -> HandlerResult<u64> { ++ self.send_message(SlaveReq::SHMEM_MAP, req, Some(&[fd.as_raw_fd()])) ++ } ++ ++ /// Handle shared memory region unmapping requests. ++ fn shmem_unmap(&self, req: &VhostUserShmemUnmapMsg) -> HandlerResult<u64> { ++ self.send_message(SlaveReq::SHMEM_UNMAP, req, None) ++ } ++ + /// Forward vhost-user-fs map file requests to the slave. + fn fs_slave_map(&self, fs: &VhostUserFSSlaveMsg, fd: &dyn AsRawFd) -> HandlerResult<u64> { + self.send_message(SlaveReq::FS_MAP, fs, Some(&[fd.as_raw_fd()])) +diff --git a/crates/vhost/src/vhost_user/slave_req_handler.rs b/crates/vhost/src/vhost_user/slave_req_handler.rs +index e998339..b84feca 100644 +--- a/crates/vhost/src/vhost_user/slave_req_handler.rs ++++ b/crates/vhost/src/vhost_user/slave_req_handler.rs +@@ -70,6 +70,7 @@ pub trait VhostUserSlaveReqHandler { + fn get_max_mem_slots(&self) -> Result<u64>; + fn add_mem_region(&self, region: &VhostUserSingleMemoryRegion, fd: File) -> Result<()>; + fn remove_mem_region(&self, region: &VhostUserSingleMemoryRegion) -> Result<()>; ++ fn get_shared_memory_regions(&self) -> Result<Vec<VhostSharedMemoryRegion>>; + } + + /// Services provided to the master by the slave without interior mutability. +@@ -118,6 +119,7 @@ pub trait VhostUserSlaveReqHandlerMut { + fn get_max_mem_slots(&mut self) -> Result<u64>; + fn add_mem_region(&mut self, region: &VhostUserSingleMemoryRegion, fd: File) -> Result<()>; + fn remove_mem_region(&mut self, region: &VhostUserSingleMemoryRegion) -> Result<()>; ++ fn get_shared_memory_regions(&mut self) -> Result<Vec<VhostSharedMemoryRegion>>; + } + + impl<T: VhostUserSlaveReqHandlerMut> VhostUserSlaveReqHandler for Mutex<T> { +@@ -226,6 +228,10 @@ impl<T: VhostUserSlaveReqHandlerMut> VhostUserSlaveReqHandler for Mutex<T> { + fn remove_mem_region(&self, region: &VhostUserSingleMemoryRegion) -> Result<()> { + self.lock().unwrap().remove_mem_region(region) + } ++ ++ fn get_shared_memory_regions(&self) -> Result<Vec<VhostSharedMemoryRegion>> { ++ self.lock().unwrap().get_shared_memory_regions() ++ } + } + + /// Server to handle service requests from masters from the master communication channel. +@@ -519,6 +525,15 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> { + let res = self.backend.remove_mem_region(&msg); + self.send_ack_message(&hdr, res)?; + } ++ Ok(MasterReq::GET_SHARED_MEMORY_REGIONS) => { ++ let regions = self.backend.get_shared_memory_regions()?; ++ let mut buf = Vec::new(); ++ let msg = VhostUserU64::new(regions.len() as u64); ++ for r in regions { ++ buf.extend_from_slice(r.as_slice()) ++ } ++ self.send_reply_with_payload(&hdr, &msg, buf.as_slice())?; ++ } + _ => { + return Err(Error::InvalidMessage); + } +-- +2.42.0 + |