Skip to content

Commit b200d76

Browse files
committed
test new iov approach
Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent 96e8a43 commit b200d76

File tree

3 files changed

+150
-12
lines changed

3 files changed

+150
-12
lines changed

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ type IoVecVec = SmallVec<[iovec; 4]>;
4040
#[derive(Debug)]
4141
pub struct IoVecBuffer {
4242
// container of the memory regions included in this IO vector
43-
vecs: IoVecVec,
43+
pub vecs: IoVecVec,
4444
// Total length of the IoVecBuffer
45-
len: u32,
45+
pub len: u32,
4646
}
4747

4848
impl IoVecBuffer {
@@ -53,9 +53,9 @@ impl IoVecBuffer {
5353

5454
let mut next_descriptor = Some(head);
5555
while let Some(desc) = next_descriptor {
56-
if desc.is_write_only() {
57-
return Err(IoVecError::WriteOnlyDescriptor);
58-
}
56+
// if desc.is_write_only() {
57+
// return Err(IoVecError::WriteOnlyDescriptor);
58+
// }
5959

6060
// We use get_slice instead of `get_host_address` here in order to have the whole
6161
// range of the descriptor chain checked, i.e. [addr, addr + len) is a valid memory

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

Lines changed: 131 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use std::io::Read;
1010
use std::mem;
1111
use std::net::Ipv4Addr;
12+
use std::os::fd::AsRawFd;
1213
use std::sync::atomic::AtomicU32;
1314
use std::sync::{Arc, Mutex};
1415

@@ -286,6 +287,7 @@ impl Net {
286287
};
287288

288289
if queue.prepare_kick(mem) {
290+
// warn!("trigger: {:?}", queue_type);
289291
self.irq_trigger
290292
.trigger_irq(IrqType::Vring)
291293
.map_err(|err| {
@@ -405,12 +407,19 @@ impl Net {
405407

406408
let queue = &mut self.queues[RX_INDEX];
407409

408-
// let mut i = 0;
410+
let mut i = 0;
411+
let mut nd = 0;
409412
let mut bytes = &self.rx_frame_buf[self.rx_bytes_send..self.rx_bytes_read];
410413
warn!("queue size: {}", queue.len(mem));
411-
warn!("do_write_frame_to_guest bytes len: {}", bytes.len());
414+
warn!(
415+
"do_write_frame_to_guest bytes s: {} e: {} len: {}",
416+
self.rx_bytes_send,
417+
self.rx_bytes_read,
418+
bytes.len()
419+
);
420+
412421
loop {
413-
// i += 1;
422+
i += 1;
414423
// warn!("do_write_frame_to_guest iteration: {i}");
415424
let head_descriptor = queue.pop_or_enable_notification(mem).ok_or_else(|| {
416425
self.metrics.no_rx_avail_buffer.inc();
@@ -469,6 +478,7 @@ impl Net {
469478
// self.rx_descriptors_used
470479
// );
471480

481+
nd = self.rx_descriptors_used;
472482
self.rx_bytes_send = 0;
473483
self.rx_header_addr = None;
474484
self.rx_descriptors_used = 0;
@@ -483,6 +493,7 @@ impl Net {
483493
}
484494
}
485495
}
496+
warn!("used {i} descriptor chains, {nd} descriptors");
486497
Ok(())
487498
}
488499

@@ -545,8 +556,15 @@ impl Net {
545556
}
546557

547558
let _metric = net_metrics.tap_write_agg.record_latency_metrics();
559+
warn!(
560+
"+++++++++++ writing to the tap iov with len: {} ++++++++",
561+
frame_iovec.iovec_count()
562+
);
563+
for v in frame_iovec.vecs.iter() {
564+
warn!("iov len: {}", v.iov_len);
565+
}
548566
match Self::write_tap(tap, frame_iovec) {
549-
Ok(_) => {
567+
Ok(l) => {
550568
let len = u64::from(frame_iovec.len());
551569
net_metrics.tx_bytes_count.add(len);
552570
net_metrics.tx_packets_count.inc();
@@ -574,20 +592,26 @@ impl Net {
574592
}
575593
} else {
576594
self.rx_bytes_read = self.read_tap().map_err(NetError::IO)?;
595+
warn!("read {} bytes from tap", self.rx_bytes_read);
577596
}
578597
Ok(())
579598
}
580599

581600
fn process_rx(&mut self) -> Result<(), DeviceError> {
582-
// warn!("process_rx");
583-
// Read as many frames as possible.
601+
self.process_rx_iov()
602+
}
584603

604+
fn process_rx_orig(&mut self) -> Result<(), DeviceError> {
605+
warn!("------- process_rx ----------");
585606
let t = std::time::Instant::now();
607+
608+
// Read as many frames as possible.
586609
loop {
587610
match self.read_from_mmds_or_tap() {
588611
Ok(_) => {
589612
self.metrics.rx_count.inc();
590613
if !self.rate_limited_rx_single_frame() {
614+
warn!("setting deferred_frame");
591615
self.rx_deferred_frame = true;
592616
break;
593617
}
@@ -603,6 +627,7 @@ impl Net {
603627
return Err(DeviceError::FailedReadTap);
604628
}
605629
};
630+
warn!("net IO error: {:?}", err);
606631
break;
607632
}
608633
Err(err) => {
@@ -617,9 +642,107 @@ impl Net {
617642
self.signal_used_queue(NetQueue::Rx)
618643
}
619644

645+
fn process_rx_iov(&mut self) -> Result<(), DeviceError> {
646+
warn!("------- process_rx ----------");
647+
let t = std::time::Instant::now();
648+
649+
let mem = self.device_state.mem().unwrap();
650+
651+
let queue_len = self.queues[RX_INDEX].len(mem);
652+
warn!("Queue len: {queue_len}");
653+
if queue_len == 0 {
654+
return Ok(());
655+
}
656+
657+
let mut used_heads = 0;
658+
loop {
659+
for _ in 0..used_heads {
660+
_ = self.queues[RX_INDEX].pop(mem);
661+
}
662+
663+
let mut the_iov = IoVecBuffer {
664+
vecs: Default::default(),
665+
len: 0,
666+
};
667+
let mut index_iov = Vec::new();
668+
let mut q = 0;
669+
while let Some(head_descriptor) = self.queues[RX_INDEX].pop(mem) {
670+
q += 1;
671+
let hi = head_descriptor.index;
672+
let iov = IoVecBuffer::from_descriptor_chain(head_descriptor).unwrap();
673+
the_iov.vecs.extend_from_slice(&iov.vecs);
674+
the_iov.len += iov.len;
675+
index_iov.push((hi, iov));
676+
}
677+
for _ in 0..q {
678+
self.queues[RX_INDEX].undo_pop();
679+
}
680+
warn!(
681+
"all_iov: vecs: {}, len: {}",
682+
the_iov.vecs.len(),
683+
the_iov.len
684+
);
685+
if index_iov.is_empty() {
686+
return Ok(());
687+
}
688+
689+
match self.tap.read_iovec(&the_iov).map_err(NetError::IO) {
690+
Ok(mut bytes_written) => {
691+
warn!("bytes written: {bytes_written}");
692+
let mut heads = 0;
693+
loop {
694+
if heads == index_iov.len() {
695+
warn!("exausted all {heads} iovecs");
696+
break;
697+
}
698+
699+
let (head_index, iov) = &index_iov[heads];
700+
701+
if bytes_written < iov.len() as usize {
702+
warn!(
703+
"head with index: {head_index} used {bytes_written} out of {} bytes",
704+
iov.len()
705+
);
706+
_ = self.queues[RX_INDEX].try_enable_notification(mem);
707+
self.queues[RX_INDEX]
708+
.add_used(mem, *head_index, bytes_written as u32)
709+
.unwrap();
710+
break;
711+
} else {
712+
warn!(
713+
"head with index: {head_index} used all out of {} bytes",
714+
iov.len()
715+
);
716+
_ = self.queues[RX_INDEX].try_enable_notification(mem);
717+
self.queues[RX_INDEX]
718+
.add_used(mem, *head_index, iov.len())
719+
.unwrap();
720+
bytes_written -= iov.len() as usize;
721+
};
722+
heads += 1;
723+
}
724+
warn!("heads: {heads}");
725+
#[allow(clippy::transmute_ptr_to_ref)]
726+
let header: &mut virtio_net_hdr_v1 =
727+
unsafe { std::mem::transmute(the_iov.vecs[0].iov_base) };
728+
header.num_buffers = heads as u16 + 1;
729+
used_heads += heads + 1;
730+
}
731+
Err(e) => {
732+
break;
733+
}
734+
}
735+
}
736+
737+
warn!("process_rx took: {}ns", t.elapsed().as_nanos());
738+
739+
self.signal_used_queue(NetQueue::Rx)?;
740+
Ok(())
741+
}
742+
620743
// Process the deferred frame first, then continue reading from tap.
621744
fn handle_deferred_frame(&mut self) -> Result<(), DeviceError> {
622-
warn!("handle_deferred_frame");
745+
// warn!("handle_deferred_frame");
623746
if self.rate_limited_rx_single_frame() {
624747
self.rx_deferred_frame = false;
625748
// process_rx() was interrupted possibly before consuming all
@@ -919,6 +1042,7 @@ impl VirtioDevice for Net {
9191042
}
9201043

9211044
fn activate(&mut self, mem: GuestMemoryMmap) -> Result<(), ActivateError> {
1045+
// warn!("NET ACKED FEATURES: {:#x}", self.acked_features);
9221046
let event_idx = self.has_feature(u64::from(VIRTIO_RING_F_EVENT_IDX));
9231047
if event_idx {
9241048
for queue in &mut self.queues {

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,20 @@ impl Tap {
185185
Ok(())
186186
}
187187

188+
/// Read an `IoVecBuffer` from tap
189+
pub(crate) fn read_iovec(&mut self, buffer: &IoVecBuffer) -> Result<usize, IoError> {
190+
let iovcnt = i32::try_from(buffer.iovec_count()).unwrap();
191+
let iov = buffer.as_iovec_ptr();
192+
193+
// SAFETY: `readv` is safe. Called with a valid tap fd, the iovec pointer and length
194+
// is provide by the `IoVecBuffer` implementation and we check the return value.
195+
let ret = unsafe { libc::readv(self.tap_file.as_raw_fd(), iov, iovcnt) };
196+
if ret == -1 {
197+
return Err(IoError::last_os_error());
198+
}
199+
Ok(usize::try_from(ret).unwrap())
200+
}
201+
188202
/// Write an `IoVecBuffer` to tap
189203
pub(crate) fn write_iovec(&mut self, buffer: &IoVecBuffer) -> Result<usize, IoError> {
190204
let iovcnt = i32::try_from(buffer.iovec_count()).unwrap();

0 commit comments

Comments
 (0)