Skip to content

Commit a905f77

Browse files
committed
feat: fix retransmission issues with MRG_RXBUF
Fix retransmission issues introduced by enabling MRG_RXBUF. Do this by tracking total capacity of the `RxBuffer` struct and only return an iov slice if it contains at least 64K bytes. Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent f72918b commit a905f77

File tree

2 files changed

+25
-12
lines changed

2 files changed

+25
-12
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -648,7 +648,10 @@ impl Net {
648648

649649
fn read_tap(&mut self) -> std::io::Result<usize> {
650650
if self.has_feature(u64::from(VIRTIO_NET_F_MRG_RXBUF)) {
651-
self.tap.read_iovec(self.rx_buffer.all_chains_mut_slice())
651+
let Some(s) = self.rx_buffer.all_chains_mut_slice() else {
652+
return Err(std::io::Error::from_raw_os_error(EAGAIN));
653+
};
654+
self.tap.read_iovec(s)
652655
} else {
653656
self.tap.read_iovec(self.rx_buffer.one_chain_mut_slice())
654657
}

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

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ pub struct RxBuffer {
7676
pub iovecs: IovRingBuffer,
7777
// Ring buffer of meta data about descriptor chains stored in the `iov_ring`.
7878
pub chain_infos: RingBuffer<ChainInfo>,
79+
// Total capacity this buffer holds.
80+
pub total_capacity: u32,
7981
// Number of descriptor chains we have used to process packets.
8082
pub used_descriptors: u16,
8183
}
@@ -87,6 +89,7 @@ impl RxBuffer {
8789
iovecs: IovRingBuffer::new()?,
8890
chain_infos: RingBuffer::new_with_size(u32::from(FIRECRACKER_MAX_QUEUE_SIZE)),
8991
used_descriptors: 0,
92+
total_capacity: 0,
9093
})
9194
}
9295

@@ -108,8 +111,12 @@ impl RxBuffer {
108111

109112
/// Returns a slice of underlying iovec for the all chains
110113
/// in the buffer.
111-
pub fn all_chains_mut_slice(&mut self) -> &mut [iovec] {
112-
self.iovecs.as_mut_slice()
114+
pub fn all_chains_mut_slice(&mut self) -> Option<&mut [iovec]> {
115+
if self.total_capacity < u32::from(u16::MAX) {
116+
None
117+
} else {
118+
Some(self.iovecs.as_mut_slice())
119+
}
113120
}
114121

115122
/// Add a new `DescriptorChain` that we received from the RX queue in the buffer.
@@ -166,6 +173,7 @@ impl RxBuffer {
166173
chain_len,
167174
chain_capacity,
168175
});
176+
self.total_capacity += chain_capacity;
169177

170178
Ok(())
171179
}
@@ -234,6 +242,7 @@ impl RxBuffer {
234242
.pop_front()
235243
.expect("This should never happen if write to the buffer succeded.");
236244
self.iovecs.pop_front(usize::from(iov_info.chain_len));
245+
self.total_capacity -= iov_info.chain_capacity;
237246

238247
if bytes_written <= iov_info.chain_capacity {
239248
write_used(iov_info.head_index, bytes_written, rx_queue);
@@ -281,7 +290,7 @@ mod tests {
281290

282291
#[test]
283292
fn test_rx_buffer_add_chain() {
284-
let mem = single_region_mem(65562);
293+
let mem = single_region_mem(65562 * 2);
285294
let rxq = VirtQueue::new(GuestAddress(0), &mem, 256);
286295
let mut queue = rxq.create_queue();
287296

@@ -316,9 +325,10 @@ mod tests {
316325
);
317326
}
318327

319-
// 16 chains of len 1
328+
// 64 chains of len 1 and size 1024
329+
// in total 64K
320330
{
321-
let chains = 16;
331+
let chains = 64;
322332
set_dtable_many_chains(&rxq, chains);
323333
queue.next_avail = Wrapping(0);
324334
let mut buff = RxBuffer::new().unwrap();
@@ -328,7 +338,7 @@ mod tests {
328338
buff.add_chain(&mem, desc).unwrap();
329339
}
330340
}
331-
let slice = buff.all_chains_mut_slice();
341+
let slice = buff.all_chains_mut_slice().unwrap();
332342
for i in 0..chains {
333343
assert_eq!(
334344
slice[i].iov_base as u64,
@@ -337,7 +347,7 @@ mod tests {
337347
);
338348
assert_eq!(slice[i].iov_len, 1024);
339349
}
340-
assert_eq!(buff.chain_infos.len(), 16);
350+
assert_eq!(buff.chain_infos.len(), chains as u32);
341351
for (i, ci) in buff.chain_infos.items[0..16].iter().enumerate() {
342352
assert_eq!(
343353
*ci,
@@ -406,11 +416,11 @@ mod tests {
406416

407417
#[test]
408418
fn test_rx_buffer_write_mrg_buf() {
409-
let mem = single_region_mem(65562);
419+
let mem = single_region_mem(65562 * 2);
410420
let rxq = VirtQueue::new(GuestAddress(0), &mem, 256);
411421
let mut queue = rxq.create_queue();
412422

413-
set_dtable_many_chains(&rxq, 2);
423+
set_dtable_many_chains(&rxq, 64);
414424

415425
let mut buff = RxBuffer::new().unwrap();
416426
while let Some(desc) = queue.pop() {
@@ -421,7 +431,7 @@ mod tests {
421431
}
422432

423433
// Initially data should be all zeros
424-
let slice = buff.all_chains_mut_slice();
434+
let slice = buff.all_chains_mut_slice().unwrap();
425435
let data_slice_before: &[u8] =
426436
// SAFETY: safe as iovecs are verified on creation.
427437
unsafe { std::slice::from_raw_parts(slice[0].iov_base.cast(), slice[0].iov_len) };
@@ -434,7 +444,7 @@ mod tests {
434444
// Write should hapepn to all 2 iovecs
435445
buff.write(&[69; 2 * 1024]).unwrap();
436446

437-
let slice = buff.all_chains_mut_slice();
447+
let slice = buff.all_chains_mut_slice().unwrap();
438448
let data_slice_after: &[u8] =
439449
// SAFETY: safe as iovecs are verified on creation.
440450
unsafe { std::slice::from_raw_parts(slice[0].iov_base.cast(), slice[0].iov_len) };

0 commit comments

Comments
 (0)