From 71b6acfd7ea0da7e4b3a345aa24b5cc1854873d4 Mon Sep 17 00:00:00 2001 From: Xingru Li Date: Mon, 26 May 2025 14:23:49 +0800 Subject: [PATCH] dragonball: vsock: support single descriptor Since kernel v6.3 the vsock packet is not split over two descriptors and is instead included in a single one. Therefore, we currently decide the specific method of obtaining BufWrapper based on the length of descriptor. Refer: https://github.com/cloud-hypervisor/cloud-hypervisor/commit/a2752fe04fdc58ae3879f45dd28816947d815362 https://git.kernel.org/torvalds/c/71dc9ec9ac7d Signed-off-by: Xingru Li [ Gao Xiang: port this patch from the internal branch to address Linux 6.1.63+. ] Signed-off-by: Gao Xiang --- .../dbs_virtio_devices/src/vsock/mod.rs | 5 +- .../dbs_virtio_devices/src/vsock/packet.rs | 123 +++++++++++++----- 2 files changed, 97 insertions(+), 31 deletions(-) diff --git a/src/dragonball/dbs_virtio_devices/src/vsock/mod.rs b/src/dragonball/dbs_virtio_devices/src/vsock/mod.rs index ab675a0cbf..433ef46589 100644 --- a/src/dragonball/dbs_virtio_devices/src/vsock/mod.rs +++ b/src/dragonball/dbs_virtio_devices/src/vsock/mod.rs @@ -14,7 +14,7 @@ mod packet; use std::os::unix::io::AsRawFd; -use vm_memory::GuestMemoryError; +use vm_memory::{GuestAddress, GuestMemoryError}; pub use self::defs::{NUM_QUEUES, QUEUE_SIZES}; pub use self::device::Vsock; @@ -136,6 +136,9 @@ pub enum VsockError { /// Encountered an unexpected read-only virtio descriptor. #[error("Encountered an unexpected read-only virtio descriptor")] UnwritableDescriptor, + /// Overflow occurred during an arithmetic operation. + #[error("Overflow occurred during an arithmetic operation, base({0:?}) + offset({0:?})")] + Overflow(GuestAddress, usize), } type Result = std::result::Result; diff --git a/src/dragonball/dbs_virtio_devices/src/vsock/packet.rs b/src/dragonball/dbs_virtio_devices/src/vsock/packet.rs index c25df09a20..095b8e5aef 100644 --- a/src/dragonball/dbs_virtio_devices/src/vsock/packet.rs +++ b/src/dragonball/dbs_virtio_devices/src/vsock/packet.rs @@ -18,7 +18,7 @@ use std::ops::{Deref, DerefMut}; use virtio_queue::{Descriptor, DescriptorChain}; -use vm_memory::GuestMemory; +use vm_memory::{Address, GuestMemory}; use super::defs; use super::{Result, VsockError}; @@ -190,15 +190,25 @@ pub struct BufWrapper { impl BufWrapper { /// Create the data wrapper from a virtq descriptor. - pub fn from_virtq_desc(desc: &Descriptor, mem: &M) -> Result { + /// Add offset and size, to support different forms of descriptor. + pub fn from_virtq_desc( + desc: &Descriptor, + mem: &M, + offset: usize, + size: usize, + ) -> Result { // Check the guest provided pointer and data size. mem.checked_offset(desc.addr(), desc.len() as usize) .ok_or_else(|| VsockError::GuestMemoryBounds(desc.addr().0, desc.len() as usize))?; Ok(Self::from_fat_ptr_unchecked( - mem.get_host_address(desc.addr()) - .map_err(VsockError::GuestMemory)?, - desc.len() as usize, + mem.get_host_address( + desc.addr() + .checked_add(offset as u64) + .ok_or(VsockError::Overflow(desc.addr(), offset))?, + ) + .map_err(VsockError::GuestMemory)?, + size, )) } @@ -265,23 +275,48 @@ impl VsockPacket { return Ok(Self { hdr, buf: None }); } - let buf_desc = desc_chain.next().ok_or(VsockError::BufDescMissing)?; + if desc.has_next() { + let buf_desc = desc_chain.next().ok_or(VsockError::BufDescMissing)?; - // All TX buffers must be readable. - if buf_desc.is_write_only() { - return Err(VsockError::UnreadableDescriptor); + // All TX buffers must be readable. + if buf_desc.is_write_only() { + return Err(VsockError::UnreadableDescriptor); + } + + // The data descriptor should be large enough to hold the data length + // indicated by the header. + if buf_desc.len() < hdr.len { + return Err(VsockError::BufDescTooSmall); + } + + Ok(Self { + hdr, + buf: Some(BufWrapper::from_virtq_desc( + &buf_desc, + desc_chain.memory(), + 0, + buf_desc.len() as usize, + )?), + }) + } else { + let buf_size = desc.len() as usize - VSOCK_PKT_HDR_SIZE; + + // The data descriptor should be large enough to hold the data length + // indicated by the header. + if buf_size < hdr.len as usize { + return Err(VsockError::BufDescTooSmall); + } + + Ok(Self { + hdr, + buf: Some(BufWrapper::from_virtq_desc( + &desc, + desc_chain.memory(), + VSOCK_PKT_HDR_SIZE, + buf_size, + )?), + }) } - - // The data descriptor should be large enough to hold the data length - // indicated by the header. - if buf_desc.len() < hdr.len { - return Err(VsockError::BufDescTooSmall); - } - - Ok(Self { - hdr, - buf: Some(BufWrapper::from_virtq_desc(&buf_desc, desc_chain.memory())?), - }) } /// Create the packet wrapper from an RX virtq chain head. @@ -301,15 +336,34 @@ impl VsockPacket { let hdr = HdrWrapper::from_virtq_desc(&desc, desc_chain.memory())?; - let buf_desc = desc_chain.next().ok_or(VsockError::BufDescMissing)?; - if !buf_desc.is_write_only() { - return Err(VsockError::UnwritableDescriptor); - } + if desc.has_next() { + let buf_desc = desc_chain.next().ok_or(VsockError::BufDescMissing)?; + if !buf_desc.is_write_only() { + return Err(VsockError::UnwritableDescriptor); + } - Ok(Self { - hdr, - buf: Some(BufWrapper::from_virtq_desc(&buf_desc, desc_chain.memory())?), - }) + Ok(Self { + hdr, + buf: Some(BufWrapper::from_virtq_desc( + &buf_desc, + desc_chain.memory(), + 0, + buf_desc.len() as usize, + )?), + }) + } else { + let buf_size = desc.len() as usize - VSOCK_PKT_HDR_SIZE; + + Ok(Self { + hdr, + buf: Some(BufWrapper::from_virtq_desc( + &desc, + desc_chain.memory(), + VSOCK_PKT_HDR_SIZE, + buf_size, + )?), + }) + } } /// Provides in-place, byte-slice, access to the vsock packet header. @@ -575,11 +629,12 @@ mod tests { // Test case: // - packet header advertises some data length; and // - the data descriptor is missing. + // This will be treated as single descriptor now. { create_context!(test_ctx, handler_ctx); set_pkt_len(1024, &handler_ctx.guest_txvq.dtable(0), &test_ctx.mem); handler_ctx.guest_txvq.dtable(0).flags().store(0); - expect_asm_error!(tx, test_ctx, handler_ctx, VsockError::BufDescMissing); + expect_asm_error!(tx, test_ctx, handler_ctx, VsockError::BufDescTooSmall); } // Test case: error on write-only buf descriptor. @@ -641,6 +696,7 @@ mod tests { } // Test case: RX descriptor chain is missing the packet buffer descriptor. + // This will be treated as single descriptor now. { create_context!(test_ctx, handler_ctx); handler_ctx @@ -648,7 +704,14 @@ mod tests { .dtable(0) .flags() .store(VIRTQ_DESC_F_WRITE); - expect_asm_error!(rx, test_ctx, handler_ctx, VsockError::BufDescMissing); + let pkt = VsockPacket::from_rx_virtq_head( + &mut handler_ctx.queues[RXQ_EVENT as usize] + .queue_mut() + .pop_descriptor_chain(&test_ctx.mem) + .unwrap(), + ) + .unwrap(); + assert_eq!(pkt.buf(), Some(vec![]).as_deref()); } }