mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-12-22 05:35:20 +00:00
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:
parent
9d5c5a6410
commit
4c22fa8df0
@ -23,7 +23,7 @@ use super::defs;
|
||||
use super::{Result, VsockError};
|
||||
use crate::get_host_address_range;
|
||||
use virtio_queue::DescriptorChain;
|
||||
use vm_memory::GuestMemory;
|
||||
use vm_memory::{Address, GuestMemory};
|
||||
use vm_virtio::{AccessPlatform, Translatable};
|
||||
|
||||
// The vsock packet header is defined by the C struct:
|
||||
@ -133,7 +133,7 @@ impl VsockPacket {
|
||||
hdr: get_host_address_range(
|
||||
desc_chain.memory(),
|
||||
head.addr()
|
||||
.translate_gva(access_platform, head.len() as usize),
|
||||
.translate_gva(access_platform, VSOCK_PKT_HDR_SIZE),
|
||||
VSOCK_PKT_HDR_SIZE,
|
||||
)
|
||||
.ok_or(VsockError::GuestMemory)?,
|
||||
@ -152,32 +152,46 @@ impl VsockPacket {
|
||||
return Err(VsockError::InvalidPktLen(pkt.len()));
|
||||
}
|
||||
|
||||
// If the packet header showed a non-zero length, there should be a data descriptor here.
|
||||
let buf_desc = desc_chain.next().ok_or(VsockError::BufDescMissing)?;
|
||||
// Prior to Linux v6.3 there are two descriptors
|
||||
if head.has_next() {
|
||||
let buf_desc = desc_chain.next().ok_or(VsockError::BufDescMissing)?;
|
||||
|
||||
// TX data should be read-only.
|
||||
if buf_desc.is_write_only() {
|
||||
return Err(VsockError::UnreadableDescriptor);
|
||||
// TX data should be read-only.
|
||||
if buf_desc.is_write_only() {
|
||||
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)
|
||||
}
|
||||
|
||||
@ -207,33 +221,53 @@ impl VsockPacket {
|
||||
return Err(VsockError::HdrDescTooSmall(head.len()));
|
||||
}
|
||||
|
||||
// All RX descriptor chains should have a header and a data descriptor.
|
||||
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;
|
||||
// Prior to Linux v6.3 there are two descriptors
|
||||
if head.has_next() {
|
||||
let buf_desc = desc_chain.next().ok_or(VsockError::BufDescMissing)?;
|
||||
let buf_size = buf_desc.len() as usize;
|
||||
|
||||
Ok(Self {
|
||||
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(
|
||||
Ok(Self {
|
||||
hdr: get_host_address_range(
|
||||
desc_chain.memory(),
|
||||
buf_desc
|
||||
.addr()
|
||||
.translate_gva(access_platform, buf_desc.len() as usize),
|
||||
buf_size,
|
||||
head.addr()
|
||||
.translate_gva(access_platform, VSOCK_PKT_HDR_SIZE),
|
||||
VSOCK_PKT_HDR_SIZE,
|
||||
)
|
||||
.ok_or(VsockError::GuestMemory)?,
|
||||
),
|
||||
buf_size,
|
||||
})
|
||||
buf: Some(
|
||||
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.
|
||||
@ -503,16 +537,6 @@ mod tests {
|
||||
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.
|
||||
{
|
||||
create_context!(test_ctx, handler_ctx);
|
||||
@ -568,15 +592,6 @@ mod tests {
|
||||
.set(VSOCK_PKT_HDR_SIZE as u32 - 1);
|
||||
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]
|
||||
|
Loading…
Reference in New Issue
Block a user