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:
a2752fe04f
https://git.kernel.org/torvalds/c/71dc9ec9ac7d

Signed-off-by: Xingru Li <lixingru.lxr@linux.alibaba.com>
[ Gao Xiang: port this patch from the internal branch to address Linux 6.1.63+. ]
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
This commit is contained in:
Xingru Li 2025-05-26 14:23:49 +08:00 committed by Gao Xiang
parent b6cafba5f6
commit 71b6acfd7e
2 changed files with 97 additions and 31 deletions

View File

@ -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<T> = std::result::Result<T, VsockError>;

View File

@ -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<M: GuestMemory>(desc: &Descriptor, mem: &M) -> Result<Self> {
/// Add offset and size, to support different forms of descriptor.
pub fn from_virtq_desc<M: GuestMemory>(
desc: &Descriptor,
mem: &M,
offset: usize,
size: usize,
) -> Result<Self> {
// 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())
mem.get_host_address(
desc.addr()
.checked_add(offset as u64)
.ok_or(VsockError::Overflow(desc.addr(), offset))?,
)
.map_err(VsockError::GuestMemory)?,
desc.len() as usize,
size,
))
}
@ -265,6 +275,7 @@ impl VsockPacket {
return Ok(Self { hdr, buf: None });
}
if desc.has_next() {
let buf_desc = desc_chain.next().ok_or(VsockError::BufDescMissing)?;
// All TX buffers must be readable.
@ -280,8 +291,32 @@ impl VsockPacket {
Ok(Self {
hdr,
buf: Some(BufWrapper::from_virtq_desc(&buf_desc, desc_chain.memory())?),
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,
)?),
})
}
}
/// Create the packet wrapper from an RX virtq chain head.
@ -301,6 +336,7 @@ impl VsockPacket {
let hdr = HdrWrapper::from_virtq_desc(&desc, desc_chain.memory())?;
if desc.has_next() {
let buf_desc = desc_chain.next().ok_or(VsockError::BufDescMissing)?;
if !buf_desc.is_write_only() {
return Err(VsockError::UnwritableDescriptor);
@ -308,8 +344,26 @@ impl VsockPacket {
Ok(Self {
hdr,
buf: Some(BufWrapper::from_virtq_desc(&buf_desc, desc_chain.memory())?),
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());
}
}