Skip to content

Commit 089f26f

Browse files
committed
Use edge triggered epoll for tap rx.
Remove register_tap_rx_listener and unregister_tap_rx_listener, and set EPOLLET flag when registering the tap fd. Edged epoll should work fine with the current code as it already tracks unconsumed input from the tap device via *deferred_frame*. When process_rx stops reading frames before EAGAIN it will set deferred_frame flag. The process_rx function will resume processing frames via RX_RATE_LIMITER_EVENT and clear that flag. Signed-off-by: Andrei Sandu <[email protected]>
1 parent 7fa4747 commit 089f26f

File tree

2 files changed

+50
-51
lines changed

2 files changed

+50
-51
lines changed

src/devices/src/virtio/net.rs

Lines changed: 49 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,6 @@ pub struct NetEpollHandler {
151151
acked_features: u64,
152152
mmds_ns: Option<MmdsNetworkStack>,
153153
guest_mac: Option<MacAddr>,
154-
epoll_fd: RawFd,
155-
rx_tap_listening: bool,
156-
rx_tap_epoll_token: u64,
157154

158155
#[cfg(test)]
159156
test_mutators: tests::TestMutators,
@@ -477,7 +474,7 @@ impl NetEpollHandler {
477474
self.tx.queue.add_used(&self.mem, head_index, 0);
478475
raise_irq = true;
479476
}
480-
477+
481478
if raise_irq {
482479
self.signal_used_queue()?;
483480
}
@@ -506,28 +503,6 @@ impl NetEpollHandler {
506503
fn read_tap(&mut self) -> io::Result<usize> {
507504
self.tap.read(&mut self.rx.frame_buf)
508505
}
509-
510-
fn register_tap_rx_listener(&mut self) -> std::result::Result<(), std::io::Error> {
511-
epoll::ctl(
512-
self.epoll_fd,
513-
epoll::ControlOptions::EPOLL_CTL_ADD,
514-
self.tap.as_raw_fd(),
515-
epoll::Event::new(epoll::Events::EPOLLIN, self.rx_tap_epoll_token),
516-
)?;
517-
self.rx_tap_listening = true;
518-
Ok(())
519-
}
520-
521-
fn unregister_tap_rx_listener(&mut self) -> std::result::Result<(), std::io::Error> {
522-
epoll::ctl(
523-
self.epoll_fd,
524-
epoll::ControlOptions::EPOLL_CTL_DEL,
525-
self.tap.as_raw_fd(),
526-
epoll::Event::new(epoll::Events::EPOLLIN, self.rx_tap_epoll_token),
527-
)?;
528-
self.rx_tap_listening = false;
529-
Ok(())
530-
}
531506
}
532507

533508
impl EpollHandler for NetEpollHandler {
@@ -547,10 +522,6 @@ impl EpollHandler for NetEpollHandler {
547522
underlying: e,
548523
})
549524
} else {
550-
if !self.rx_tap_listening {
551-
self.register_tap_rx_listener()
552-
.map_err(DeviceError::IoError)?;
553-
}
554525
// If the limiter is not blocked, resume the receiving of bytes.
555526
if !self.rx.rate_limiter.is_blocked() {
556527
// There should be a buffer available now to receive the frame into.
@@ -564,8 +535,6 @@ impl EpollHandler for NetEpollHandler {
564535
METRICS.net.rx_tap_event_count.inc();
565536

566537
if self.rx.queue.is_empty(&self.mem) {
567-
self.unregister_tap_rx_listener()
568-
.map_err(DeviceError::IoError)?;
569538
return Err(DeviceError::NoAvailBuffers);
570539
}
571540

@@ -827,6 +796,8 @@ impl VirtioDevice for Net {
827796
} else {
828797
None
829798
};
799+
let tap_fd = tap.as_raw_fd();
800+
830801
let handler = NetEpollHandler {
831802
rx: RxVirtio::new(
832803
rx_queue,
@@ -845,9 +816,6 @@ impl VirtioDevice for Net {
845816
acked_features: self.acked_features,
846817
mmds_ns,
847818
guest_mac: self.guest_mac(),
848-
epoll_fd: self.epoll_config.epoll_raw_fd,
849-
rx_tap_listening: false,
850-
rx_tap_epoll_token: self.epoll_config.rx_tap_token,
851819

852820
#[cfg(test)]
853821
test_mutators: tests::TestMutators::default(),
@@ -867,6 +835,20 @@ impl VirtioDevice for Net {
867835

868836
//TODO: barrier needed here maybe?
869837

838+
epoll::ctl(
839+
self.epoll_config.epoll_raw_fd,
840+
epoll::ControlOptions::EPOLL_CTL_ADD,
841+
tap_fd,
842+
epoll::Event::new(
843+
epoll::Events::EPOLLIN | epoll::Events::EPOLLET,
844+
self.epoll_config.rx_tap_token,
845+
),
846+
)
847+
.map_err(|e| {
848+
METRICS.net.activate_fails.inc();
849+
ActivateError::EpollCtl(e)
850+
})?;
851+
870852
epoll::ctl(
871853
self.epoll_config.epoll_raw_fd,
872854
epoll::ControlOptions::EPOLL_CTL_ADD,
@@ -1130,7 +1112,6 @@ mod tests {
11301112
let interrupt_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
11311113
let rx_queue_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
11321114
let tx_queue_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
1133-
let epoll_fd = epoll::create(true).unwrap();
11341115

11351116
(
11361117
NetEpollHandler {
@@ -1144,9 +1125,6 @@ mod tests {
11441125
mmds_ns: Some(MmdsNetworkStack::new_with_defaults()),
11451126
test_mutators,
11461127
guest_mac: None,
1147-
epoll_fd,
1148-
rx_tap_epoll_token: 0,
1149-
rx_tap_listening: false,
11501128
},
11511129
txq,
11521130
rxq,
@@ -1482,15 +1460,12 @@ mod tests {
14821460
};
14831461
let mem = GuestMemory::new(&[(GuestAddress(0), 0x10000)]).unwrap();
14841462
let (mut h, _txq, rxq) = default_test_netepollhandler(&mem, test_mutators);
1485-
h.register_tap_rx_listener().unwrap();
14861463

14871464
// The RX queue is empty.
14881465
match h.handle_event(RX_TAP_EVENT, epoll::Events::EPOLLIN) {
14891466
Err(DeviceError::NoAvailBuffers) => (),
14901467
_ => panic!("invalid"),
14911468
}
1492-
// Since the RX was empty, we shouldn't be listening for tap RX events.
1493-
assert!(!h.rx_tap_listening);
14941469

14951470
// Fake an avail buffer; this time, tap reading should error out.
14961471
rxq.avail.idx.set(1);
@@ -1711,8 +1686,8 @@ mod tests {
17111686
}
17121687

17131688
// wait for 100ms to give the rate-limiter timer a chance to replenish
1714-
// wait for an extra 50ms to make sure the timerfd event makes its way from the kernel
1715-
thread::sleep(Duration::from_millis(150));
1689+
// wait for an extra 100ms to make sure the timerfd event makes its way from the kernel
1690+
thread::sleep(Duration::from_millis(200));
17161691

17171692
// following TX procedure should succeed because bandwidth should now be available
17181693
{
@@ -1762,8 +1737,8 @@ mod tests {
17621737
}
17631738

17641739
// wait for 100ms to give the rate-limiter timer a chance to replenish
1765-
// wait for an extra 50ms to make sure the timerfd event makes its way from the kernel
1766-
thread::sleep(Duration::from_millis(150));
1740+
// wait for an extra 100ms to make sure the timerfd event makes its way from the kernel
1741+
thread::sleep(Duration::from_millis(200));
17671742

17681743
// following RX procedure should succeed because bandwidth should now be available
17691744
{
@@ -1819,8 +1794,8 @@ mod tests {
18191794
}
18201795

18211796
// wait for 100ms to give the rate-limiter timer a chance to replenish
1822-
// wait for an extra 50ms to make sure the timerfd event makes its way from the kernel
1823-
thread::sleep(Duration::from_millis(150));
1797+
// wait for an extra 100ms to make sure the timerfd event makes its way from the kernel
1798+
thread::sleep(Duration::from_millis(200));
18241799

18251800
// following TX procedure should succeed because ops should now be available
18261801
{
@@ -1874,8 +1849,8 @@ mod tests {
18741849
}
18751850

18761851
// wait for 100ms to give the rate-limiter timer a chance to replenish
1877-
// wait for an extra 50ms to make sure the timerfd event makes its way from the kernel
1878-
thread::sleep(Duration::from_millis(150));
1852+
// wait for an extra 100ms to make sure the timerfd event makes its way from the kernel
1853+
thread::sleep(Duration::from_millis(200));
18791854

18801855
// following RX procedure should succeed because ops should now be available
18811856
{
@@ -1924,4 +1899,28 @@ mod tests {
19241899
compare_buckets(h.get_tx_rate_limiter().bandwidth().unwrap(), &tx_bytes);
19251900
compare_buckets(h.get_tx_rate_limiter().ops().unwrap(), &tx_ops);
19261901
}
1902+
1903+
#[test]
1904+
fn test_tx_queue_interrupt() {
1905+
// Regression test for https://github.com/firecracker-microvm/firecracker/issues/1436 .
1906+
let mem = GuestMemory::new(&[(GuestAddress(0), 0x10000)]).unwrap();
1907+
let (mut h, txq, _) = default_test_netepollhandler(&mem, TestMutators::default());
1908+
1909+
let daddr = 0x2000;
1910+
assert!(daddr > txq.end().0);
1911+
1912+
// Do some TX.
1913+
txq.avail.idx.set(1);
1914+
txq.avail.ring[0].set(0);
1915+
txq.dtable[0].set(daddr, 0x1000, 0, 0);
1916+
1917+
// trigger the TX handler
1918+
h.tx.queue_evt.write(1).unwrap();
1919+
h.handle_event(TX_QUEUE_EVENT, EPOLLIN).unwrap();
1920+
1921+
// Verify if TX queue was processed.
1922+
assert_eq!(txq.used.idx.get(), 1);
1923+
// Check if interrupt was triggered.
1924+
assert_eq!(h.interrupt_evt.read().unwrap(), 1);
1925+
}
19271926
}

tests/integration_tests/build/test_coverage.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
import host_tools.cargo_build as host # pylint: disable=import-error
2121

22-
COVERAGE_TARGET_PCT = 85.2
22+
COVERAGE_TARGET_PCT = 85.1
2323
COVERAGE_MAX_DELTA = 0.01
2424

2525
CARGO_KCOV_REL_PATH = os.path.join(host.CARGO_BUILD_REL_PATH, 'kcov')

0 commit comments

Comments
 (0)