Skip to content

Commit f1d07a2

Browse files
committed
feat(iovec): add support for VIRTQ_DESC_F_INDIRECT to IoVecBuffer
Now IoVecBuffer/Mut can be built from descriptor chains utilizing VIRTQ_DESC_F_INDIRECT flag. The way indirect descriptors work is: - Descriptors from descriptor table instead of pointing to the buffers where data needs to be written to/read from now can point to buffers that contain other descriptor table. That 'indirect' descriptor table contains descriptor which point to actual buffers for data. - All descriptor in the 'indirect' descriptor table are processed sequentially. - The `VIRTQ_DESC_F_WRITE` flag is ignored for the main descriptor (the one from original descriptor table) Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent df8cad5 commit f1d07a2

File tree

4 files changed

+101
-23
lines changed

4 files changed

+101
-23
lines changed

src/vmm/src/devices/virtio/iovec.rs

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ use libc::{c_void, iovec, size_t};
77
use serde::{Deserialize, Serialize};
88
use vm_memory::bitmap::Bitmap;
99
use vm_memory::{
10-
GuestMemory, GuestMemoryError, ReadVolatile, VolatileMemoryError, VolatileSlice, WriteVolatile,
10+
GuestAddress, GuestMemory, GuestMemoryError, ReadVolatile, VolatileMemoryError, VolatileSlice,
11+
WriteVolatile,
1112
};
1213

1314
use super::iov_deque::{IovDeque, IovDequeError};
15+
use super::queue::Descriptor;
1416
use crate::devices::virtio::queue::DescriptorChain;
1517
use crate::vstate::memory::GuestMemoryMmap;
1618

@@ -24,6 +26,8 @@ pub enum IoVecError {
2426
OverflowedDescriptor,
2527
/// Tried to push to full IovDeque.
2628
IovDequeOverflow,
29+
/// Nested indirect descriptor
30+
NestedIndirectDescriptor,
2731
/// Guest memory error: {0}
2832
GuestMemory(#[from] GuestMemoryError),
2933
/// Error with underlying `IovDeque`: {0}
@@ -62,33 +66,75 @@ impl IoVecBuffer {
6266

6367
let mut next_descriptor = Some(head);
6468
while let Some(desc) = next_descriptor {
65-
if desc.is_write_only() {
66-
return Err(IoVecError::WriteOnlyDescriptor);
67-
}
69+
if desc.is_indirect() {
70+
// We use get_slice instead of `get_host_address` here in order to have the whole
71+
// range of the descriptor chain checked, i.e. [addr, addr + len) is a valid memory
72+
// region in the GuestMemoryMmap.
73+
let indirect_desc_slice = mem
74+
.get_slice(desc.addr, desc.len as usize)
75+
.map_err(IoVecError::GuestMemory)?;
76+
77+
// SAFETY:
78+
// We checked the slice above. We just transform it into
79+
// a slice of Descriptors.
80+
let indirect_desc_slice: &[Descriptor] = unsafe {
81+
std::slice::from_raw_parts(
82+
indirect_desc_slice.ptr_guard().as_ptr().cast(),
83+
desc.len as usize / std::mem::size_of::<Descriptor>(),
84+
)
85+
};
6886

69-
// We use get_slice instead of `get_host_address` here in order to have the whole
70-
// range of the descriptor chain checked, i.e. [addr, addr + len) is a valid memory
71-
// region in the GuestMemoryMmap.
72-
let iov_base = mem
73-
.get_slice(desc.addr, desc.len as usize)?
74-
.ptr_guard_mut()
75-
.as_ptr()
76-
.cast::<c_void>();
77-
self.vecs.push(iovec {
78-
iov_base,
79-
iov_len: desc.len as size_t,
80-
});
81-
self.len = self
82-
.len
83-
.checked_add(desc.len)
84-
.ok_or(IoVecError::OverflowedDescriptor)?;
87+
for d in indirect_desc_slice.iter() {
88+
if desc.is_write_only() {
89+
return Err(IoVecError::WriteOnlyDescriptor);
90+
}
91+
if d.is_indirect() {
92+
return Err(IoVecError::NestedIndirectDescriptor);
93+
}
94+
self.add_descriptor(mem, GuestAddress(d.addr), d.len)?;
95+
if !d.has_next() {
96+
break;
97+
}
98+
}
99+
} else {
100+
if desc.is_write_only() {
101+
return Err(IoVecError::WriteOnlyDescriptor);
102+
}
103+
104+
self.add_descriptor(mem, desc.addr, desc.len)?;
105+
}
85106

86107
next_descriptor = desc.next_descriptor();
87108
}
88109

89110
Ok(())
90111
}
91112

113+
fn add_descriptor(
114+
&mut self,
115+
mem: &GuestMemoryMmap,
116+
addr: GuestAddress,
117+
len: u32,
118+
) -> Result<(), IoVecError> {
119+
// We use get_slice instead of `get_host_address` here in order to have the whole
120+
// range of the descriptor chain checked, i.e. [addr, addr + len) is a valid memory
121+
// region in the GuestMemoryMmap.
122+
let iov_base = mem
123+
.get_slice(addr, len as usize)?
124+
.ptr_guard_mut()
125+
.as_ptr()
126+
.cast::<c_void>();
127+
self.vecs.push(iovec {
128+
iov_base,
129+
iov_len: len as size_t,
130+
});
131+
self.len = self
132+
.len
133+
.checked_add(len)
134+
.ok_or(IoVecError::OverflowedDescriptor)?;
135+
Ok(())
136+
}
137+
92138
/// Create an `IoVecBuffer` from a `DescriptorChain`
93139
///
94140
/// # Safety

src/vmm/src/devices/virtio/net/device.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ use crate::devices::virtio::gen::virtio_net::{
2121
VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4,
2222
VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MRG_RXBUF,
2323
};
24-
use crate::devices::virtio::gen::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
24+
use crate::devices::virtio::gen::virtio_ring::{
25+
VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC,
26+
};
2527
use crate::devices::virtio::iovec::{
2628
IoVecBuffer, IoVecBufferMut, IoVecError, ParsedDescriptorChain,
2729
};
@@ -281,9 +283,10 @@ impl Net {
281283
| 1 << VIRTIO_NET_F_HOST_TSO4
282284
| 1 << VIRTIO_NET_F_HOST_TSO6
283285
| 1 << VIRTIO_NET_F_HOST_UFO
284-
| 1 << VIRTIO_F_VERSION_1
285286
| 1 << VIRTIO_NET_F_MRG_RXBUF
286-
| 1 << VIRTIO_RING_F_EVENT_IDX;
287+
| 1 << VIRTIO_RING_F_INDIRECT_DESC
288+
| 1 << VIRTIO_RING_F_EVENT_IDX
289+
| 1 << VIRTIO_F_VERSION_1;
287290

288291
let mut config_space = ConfigSpace::default();
289292
if let Some(mac) = guest_mac {

src/vmm/src/devices/virtio/queue.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::vstate::memory::{Address, Bitmap, ByteValued, GuestAddress, GuestMemo
1414

1515
pub const VIRTQ_DESC_F_NEXT: u16 = 0x1;
1616
pub const VIRTQ_DESC_F_WRITE: u16 = 0x2;
17+
pub const VIRTQ_DESC_F_INDIRECT: u16 = 0x4;
1718

1819
/// Max size of virtio queues offered by firecracker's virtio devices.
1920
pub(super) const FIRECRACKER_MAX_QUEUE_SIZE: u16 = 256;
@@ -49,6 +50,26 @@ pub struct Descriptor {
4950
pub next: u16,
5051
}
5152

53+
impl Descriptor {
54+
/// Gets if this descriptor chain has another descriptor chain linked after it.
55+
pub fn has_next(&self) -> bool {
56+
self.flags & VIRTQ_DESC_F_NEXT != 0
57+
}
58+
59+
/// If the driver designated this as a write only descriptor.
60+
///
61+
/// If this is false, this descriptor is read only.
62+
/// Write only means the emulated device can write and the driver can read.
63+
pub fn is_write_only(&self) -> bool {
64+
self.flags & VIRTQ_DESC_F_WRITE != 0
65+
}
66+
67+
/// If the driver designated this as a indirect descriptor.
68+
pub fn is_indirect(&self) -> bool {
69+
self.flags & VIRTQ_DESC_F_INDIRECT != 0
70+
}
71+
}
72+
5273
// SAFETY: `Descriptor` is a POD and contains no padding.
5374
unsafe impl ByteValued for Descriptor {}
5475

@@ -138,6 +159,11 @@ impl DescriptorChain {
138159
self.flags & VIRTQ_DESC_F_WRITE != 0
139160
}
140161

162+
/// If the driver designated this as a indirect descriptor.
163+
pub fn is_indirect(&self) -> bool {
164+
self.flags & VIRTQ_DESC_F_INDIRECT != 0
165+
}
166+
141167
/// Gets the next descriptor in this descriptor chain, if there is one.
142168
///
143169
/// Note that this is distinct from the next descriptor chain returned by `AvailIter`, which is

src/vmm/src/devices/virtio/vsock/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ pub enum VsockError {
125125
DescChainTooShortForHeader(usize),
126126
/// The descriptor chain length was greater than the max ([u32::MAX])
127127
DescChainOverflow,
128+
/// Nested indirect descriptor
129+
NestedIndirectDescriptor,
128130
/// The vsock header `len` field holds an invalid value: {0}
129131
InvalidPktLen(u32),
130132
/// A data fetch was attempted when no data was available.
@@ -154,6 +156,7 @@ impl From<IoVecError> for VsockError {
154156
IoVecError::OverflowedDescriptor => VsockError::DescChainOverflow,
155157
IoVecError::IovDeque(err) => VsockError::IovDeque(err),
156158
IoVecError::IovDequeOverflow => VsockError::IovDequeOverflow,
159+
IoVecError::NestedIndirectDescriptor => VsockError::NestedIndirectDescriptor,
157160
}
158161
}
159162
}

0 commit comments

Comments
 (0)