virtio-devices: 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.

This change is based on the discovery and fix identified by Stefano
Garzarella for the vm-virtio vsock implementation and adapted for our
very different codebase.

Fixes: #5691

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
This commit is contained in:
Rob Bradford 2023-08-21 16:22:52 +01:00 committed by Bo Chen
parent cc5dd8c297
commit a2752fe04f

View File

@ -23,7 +23,7 @@ use super::defs;
use super::{Result, VsockError}; use super::{Result, VsockError};
use crate::get_host_address_range; use crate::get_host_address_range;
use virtio_queue::DescriptorChain; use virtio_queue::DescriptorChain;
use vm_memory::GuestMemory; use vm_memory::{Address, GuestMemory};
use vm_virtio::{AccessPlatform, Translatable}; use vm_virtio::{AccessPlatform, Translatable};
// The vsock packet header is defined by the C struct: // The vsock packet header is defined by the C struct:
@ -133,7 +133,7 @@ impl VsockPacket {
hdr: get_host_address_range( hdr: get_host_address_range(
desc_chain.memory(), desc_chain.memory(),
head.addr() head.addr()
.translate_gva(access_platform, head.len() as usize), .translate_gva(access_platform, VSOCK_PKT_HDR_SIZE),
VSOCK_PKT_HDR_SIZE, VSOCK_PKT_HDR_SIZE,
) )
.ok_or(VsockError::GuestMemory)?, .ok_or(VsockError::GuestMemory)?,
@ -152,32 +152,46 @@ impl VsockPacket {
return Err(VsockError::InvalidPktLen(pkt.len())); return Err(VsockError::InvalidPktLen(pkt.len()));
} }
// If the packet header showed a non-zero length, there should be a data descriptor here. // Prior to Linux v6.3 there are two descriptors
let buf_desc = desc_chain.next().ok_or(VsockError::BufDescMissing)?; if head.has_next() {
let buf_desc = desc_chain.next().ok_or(VsockError::BufDescMissing)?;
// TX data should be read-only. // TX data should be read-only.
if buf_desc.is_write_only() { if buf_desc.is_write_only() {
return Err(VsockError::UnreadableDescriptor); return Err(VsockError::UnreadableDescriptor);
}
// The data buffer should be large enough to fit the size of the data, as described by
// the header descriptor.
if buf_desc.len() < pkt.len() {
return Err(VsockError::BufDescTooSmall);
}
let buf_size = buf_desc.len() as usize;
pkt.buf_size = buf_size;
pkt.buf = Some(
get_host_address_range(
desc_chain.memory(),
buf_desc.addr().translate_gva(access_platform, buf_size),
pkt.buf_size,
)
.ok_or(VsockError::GuestMemory)?,
);
} else {
let buf_size: usize = head.len() as usize - VSOCK_PKT_HDR_SIZE;
pkt.buf_size = buf_size;
pkt.buf = Some(
get_host_address_range(
desc_chain.memory(),
head.addr()
.checked_add(VSOCK_PKT_HDR_SIZE as u64)
.unwrap()
.translate_gva(access_platform, buf_size),
buf_size,
)
.ok_or(VsockError::GuestMemory)?,
);
} }
// The data buffer should be large enough to fit the size of the data, as described by
// the header descriptor.
if buf_desc.len() < pkt.len() {
return Err(VsockError::BufDescTooSmall);
}
pkt.buf_size = buf_desc.len() as usize;
pkt.buf = Some(
get_host_address_range(
desc_chain.memory(),
buf_desc
.addr()
.translate_gva(access_platform, buf_desc.len() as usize),
pkt.buf_size,
)
.ok_or(VsockError::GuestMemory)?,
);
Ok(pkt) Ok(pkt)
} }
@ -207,33 +221,53 @@ impl VsockPacket {
return Err(VsockError::HdrDescTooSmall(head.len())); return Err(VsockError::HdrDescTooSmall(head.len()));
} }
// All RX descriptor chains should have a header and a data descriptor. // Prior to Linux v6.3 there are two descriptors
if !head.has_next() { if head.has_next() {
return Err(VsockError::BufDescMissing); let buf_desc = desc_chain.next().ok_or(VsockError::BufDescMissing)?;
} let buf_size = buf_desc.len() as usize;
let buf_desc = desc_chain.next().ok_or(VsockError::BufDescMissing)?;
let buf_size = buf_desc.len() as usize;
Ok(Self { Ok(Self {
hdr: get_host_address_range( hdr: get_host_address_range(
desc_chain.memory(),
head.addr()
.translate_gva(access_platform, head.len() as usize),
VSOCK_PKT_HDR_SIZE,
)
.ok_or(VsockError::GuestMemory)?,
buf: Some(
get_host_address_range(
desc_chain.memory(), desc_chain.memory(),
buf_desc head.addr()
.addr() .translate_gva(access_platform, VSOCK_PKT_HDR_SIZE),
.translate_gva(access_platform, buf_desc.len() as usize), VSOCK_PKT_HDR_SIZE,
buf_size,
) )
.ok_or(VsockError::GuestMemory)?, .ok_or(VsockError::GuestMemory)?,
), buf: Some(
buf_size, get_host_address_range(
}) desc_chain.memory(),
buf_desc.addr().translate_gva(access_platform, buf_size),
buf_size,
)
.ok_or(VsockError::GuestMemory)?,
),
buf_size,
})
} else {
let buf_size: usize = head.len() as usize - VSOCK_PKT_HDR_SIZE;
Ok(Self {
hdr: get_host_address_range(
desc_chain.memory(),
head.addr()
.translate_gva(access_platform, VSOCK_PKT_HDR_SIZE),
VSOCK_PKT_HDR_SIZE,
)
.ok_or(VsockError::GuestMemory)?,
buf: Some(
get_host_address_range(
desc_chain.memory(),
head.addr()
.checked_add(VSOCK_PKT_HDR_SIZE as u64)
.unwrap()
.translate_gva(access_platform, buf_size),
buf_size,
)
.ok_or(VsockError::GuestMemory)?,
),
buf_size,
})
}
} }
/// Provides in-place, byte-slice, access to the vsock packet header. /// Provides in-place, byte-slice, access to the vsock packet header.
@ -503,16 +537,6 @@ mod tests {
expect_asm_error!(tx, test_ctx, handler_ctx, VsockError::InvalidPktLen(_)); expect_asm_error!(tx, test_ctx, handler_ctx, VsockError::InvalidPktLen(_));
} }
// Test case:
// - packet header advertises some data length; and
// - the data descriptor is missing.
{
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.set(0);
expect_asm_error!(tx, test_ctx, handler_ctx, VsockError::BufDescMissing);
}
// Test case: error on write-only buf descriptor. // Test case: error on write-only buf descriptor.
{ {
create_context!(test_ctx, handler_ctx); create_context!(test_ctx, handler_ctx);
@ -568,15 +592,6 @@ mod tests {
.set(VSOCK_PKT_HDR_SIZE as u32 - 1); .set(VSOCK_PKT_HDR_SIZE as u32 - 1);
expect_asm_error!(rx, test_ctx, handler_ctx, VsockError::HdrDescTooSmall(_)); expect_asm_error!(rx, test_ctx, handler_ctx, VsockError::HdrDescTooSmall(_));
} }
// Test case: RX descriptor chain is missing the packet buffer descriptor.
{
create_context!(test_ctx, handler_ctx);
handler_ctx.guest_rxvq.dtable[0]
.flags
.set(VRING_DESC_F_WRITE.try_into().unwrap());
expect_asm_error!(rx, test_ctx, handler_ctx, VsockError::BufDescMissing);
}
} }
#[test] #[test]