Skip to content

Commit 4ef5e26

Browse files
committed
feat: impl basic iovec ability for IoVecBufferMut
Add load_descriptor_chain and other essential methods for IoVecBufferMut. Signed-off-by: ihciah <[email protected]>
1 parent cbfc995 commit 4ef5e26

File tree

3 files changed

+85
-16
lines changed

3 files changed

+85
-16
lines changed

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

Lines changed: 77 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ pub struct IoVecBuffer {
4545
len: u32,
4646
}
4747

48+
impl IoVecBuffer {
49+
/// Create an empty `IoVecBuffer` with a given capacity.
50+
pub fn with_capacity(cap: usize) -> IoVecBuffer {
51+
IoVecBuffer {
52+
vecs: IoVecVec::with_capacity(cap),
53+
len: 0,
54+
}
55+
}
56+
}
57+
4858
// SAFETY: `IoVecBuffer` doesn't allow for interior mutability and no shared ownership is possible
4959
// as it doesn't implement clone
5060
unsafe impl Send for IoVecBuffer {}
@@ -218,21 +228,39 @@ impl IoVecBuffer {
218228
/// It describes a write-only buffer passed to us by the guest that is scattered across multiple
219229
/// memory regions. Additionally, this wrapper provides methods that allow reading arbitrary ranges
220230
/// of data from that buffer.
221-
#[derive(Debug)]
231+
#[derive(Debug, Default)]
222232
pub struct IoVecBufferMut {
223233
// container of the memory regions included in this IO vector
224234
vecs: IoVecVec,
225235
// Total length of the IoVecBufferMut
226236
len: u32,
227237
}
228238

239+
impl IoVecBufferMut {
240+
/// Create an empty `IoVecBufferMut` with a given capacity.
241+
pub fn with_capacity(cap: usize) -> IoVecBufferMut {
242+
IoVecBufferMut {
243+
vecs: IoVecVec::with_capacity(cap),
244+
len: 0,
245+
}
246+
}
247+
}
248+
249+
// SAFETY: iovec pointers are safe to send across threads.
250+
unsafe impl Send for IoVecBufferMut {}
251+
229252
impl IoVecBufferMut {
230253
/// Create an `IoVecBufferMut` from a `DescriptorChain`
231-
pub fn from_descriptor_chain(head: DescriptorChain) -> Result<Self, IoVecError> {
232-
let mut vecs = IoVecVec::new();
233-
let mut len = 0u32;
254+
/// # Safety
255+
/// The descriptor chain cannot be referencing the same memory location as another chain.
256+
pub unsafe fn load_descriptor_chain(
257+
&mut self,
258+
mut desc: DescriptorChain,
259+
max_size: Option<u32>,
260+
) -> Result<(), IoVecError> {
261+
self.clear();
234262

235-
for desc in head {
263+
loop {
236264
if !desc.is_write_only() {
237265
return Err(IoVecError::ReadOnlyDescriptor);
238266
}
@@ -248,23 +276,57 @@ impl IoVecBufferMut {
248276
slice.bitmap().mark_dirty(0, desc.len as usize);
249277

250278
let iov_base = slice.ptr_guard_mut().as_ptr().cast::<c_void>();
251-
vecs.push(iovec {
279+
self.vecs.push(iovec {
252280
iov_base,
253281
iov_len: desc.len as size_t,
254282
});
255-
len = len
283+
self.len = self
284+
.len
256285
.checked_add(desc.len)
257286
.ok_or(IoVecError::OverflowedDescriptor)?;
287+
if matches!(max_size, Some(max) if self.len >= max) {
288+
break;
289+
}
290+
if desc.load_next_descriptor().is_none() {
291+
break;
292+
}
258293
}
259294

260-
Ok(Self { vecs, len })
295+
Ok(())
296+
}
297+
298+
/// Create an `IoVecBufferMut` from a `DescriptorChain`
299+
/// # Safety
300+
/// The descriptor chain cannot be referencing the same memory location as another chain.
301+
pub unsafe fn from_descriptor_chain(head: DescriptorChain) -> Result<Self, IoVecError> {
302+
let mut new_buffer = Self::default();
303+
304+
Self::load_descriptor_chain(&mut new_buffer, head, None)?;
305+
306+
Ok(new_buffer)
261307
}
262308

263309
/// Get the total length of the memory regions covered by this `IoVecBuffer`
264310
pub(crate) fn len(&self) -> u32 {
265311
self.len
266312
}
267313

314+
/// Returns a pointer to the memory keeping the `iovec` structs
315+
pub fn as_iovec_ptr(&self) -> *const iovec {
316+
self.vecs.as_ptr()
317+
}
318+
319+
/// Returns the length of the `iovec` array.
320+
pub fn iovec_count(&self) -> usize {
321+
self.vecs.len()
322+
}
323+
324+
/// Clears the `iovec` array
325+
pub fn clear(&mut self) {
326+
self.vecs.clear();
327+
self.len = 0u32;
328+
}
329+
268330
/// Writes a number of bytes into the `IoVecBufferMut` starting at a given offset.
269331
///
270332
/// This will try to fill `IoVecBufferMut` writing bytes from the `buf` starting from
@@ -469,11 +531,13 @@ mod tests {
469531

470532
let (mut q, _) = read_only_chain(&mem);
471533
let head = q.pop(&mem).unwrap();
472-
IoVecBufferMut::from_descriptor_chain(head).unwrap_err();
534+
// SAFETY: This descriptor chain is only loaded into one buffer.
535+
unsafe { IoVecBufferMut::from_descriptor_chain(head).unwrap_err() };
473536

474537
let (mut q, _) = write_only_chain(&mem);
475538
let head = q.pop(&mem).unwrap();
476-
IoVecBufferMut::from_descriptor_chain(head).unwrap();
539+
// SAFETY: This descriptor chain is only loaded into one buffer.
540+
unsafe { IoVecBufferMut::from_descriptor_chain(head).unwrap() };
477541
}
478542

479543
#[test]
@@ -494,7 +558,7 @@ mod tests {
494558
let head = q.pop(&mem).unwrap();
495559

496560
// SAFETY: This descriptor chain is only loaded once in this test
497-
let iovec = IoVecBufferMut::from_descriptor_chain(head).unwrap();
561+
let iovec = unsafe { IoVecBufferMut::from_descriptor_chain(head).unwrap() };
498562
assert_eq!(iovec.len(), 4 * 64);
499563
}
500564

@@ -559,7 +623,8 @@ mod tests {
559623
// This is a descriptor chain with 4 elements 64 bytes long each.
560624
let head = q.pop(&mem).unwrap();
561625

562-
let mut iovec = IoVecBufferMut::from_descriptor_chain(head).unwrap();
626+
// SAFETY: This descriptor chain is only loaded into one buffer.
627+
let mut iovec = unsafe { IoVecBufferMut::from_descriptor_chain(head).unwrap() };
563628
let buf = vec![0u8, 1, 2, 3, 4];
564629

565630
// One test vector for each part of the chain

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ impl Entropy {
132132
let index = desc.index;
133133
METRICS.entropy_event_count.inc();
134134

135-
let bytes = match IoVecBufferMut::from_descriptor_chain(desc) {
135+
// SAFETY: This descriptor chain is only loaded into one buffer.
136+
let bytes = match unsafe { IoVecBufferMut::from_descriptor_chain(desc) } {
136137
Ok(mut iovec) => {
137138
debug!(
138139
"entropy: guest request for {} bytes of entropy",
@@ -428,13 +429,15 @@ mod tests {
428429
// This should succeed, we just added two descriptors
429430
let desc = entropy_dev.queues_mut()[RNG_QUEUE].pop(&mem).unwrap();
430431
assert!(matches!(
431-
IoVecBufferMut::from_descriptor_chain(desc),
432+
// SAFETY: This descriptor chain is only loaded into one buffer.
433+
unsafe { IoVecBufferMut::from_descriptor_chain(desc) },
432434
Err(crate::devices::virtio::iovec::IoVecError::ReadOnlyDescriptor)
433435
));
434436

435437
// This should succeed, we should have one more descriptor
436438
let desc = entropy_dev.queues_mut()[RNG_QUEUE].pop(&mem).unwrap();
437-
let mut iovec = IoVecBufferMut::from_descriptor_chain(desc).unwrap();
439+
// SAFETY: This descriptor chain is only loaded into one buffer.
440+
let mut iovec = unsafe { IoVecBufferMut::from_descriptor_chain(desc).unwrap() };
438441
entropy_dev.handle_one(&mut iovec).unwrap();
439442
}
440443

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,8 @@ impl VsockPacket {
161161
/// Returns [`VsockError::DescChainTooShortForHeader`] if the descriptor chain's total buffer
162162
/// length is insufficient to hold the 44 byte vsock header
163163
pub fn from_rx_virtq_head(chain: DescriptorChain) -> Result<Self, VsockError> {
164-
let buffer = IoVecBufferMut::from_descriptor_chain(chain)?;
164+
// SAFETY: This descriptor chain is only loaded into one buffer.
165+
let buffer = unsafe { IoVecBufferMut::from_descriptor_chain(chain)? };
165166

166167
if buffer.len() < VSOCK_PKT_HDR_SIZE {
167168
return Err(VsockError::DescChainTooShortForHeader(buffer.len() as usize));

0 commit comments

Comments
 (0)