Skip to content

Commit 8412666

Browse files
authored
xdp: optimize tx_loop (anza-xyz#7562)
* xdp: switch kick from closure to fn so we can explicitly inline Looks like LLVM wasn't inlining the closure, meh * xdp: reduce the number of atomic ops Only sync tx and completion when absolutely necessary. This reduces the number of atomic ops and cache syncs.
1 parent f149dec commit 8412666

1 file changed

Lines changed: 50 additions & 42 deletions

File tree

xdp/src/tx_loop.rs

Lines changed: 50 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -133,30 +133,6 @@ pub fn tx_loop<T: AsRef<[u8]>, A: AsRef<[SocketAddr]>>(
133133
// packets.
134134
let mut batched_packets = 0;
135135

136-
// With some drivers, or always when we work in SKB mode, we need to explicitly kick the driver
137-
// once we want the NIC to do something.
138-
let kick = |ring: &TxRing<SliceUmemFrame<'_>>| {
139-
if !ring.needs_wakeup() {
140-
return;
141-
}
142-
143-
if let Err(e) = ring.wake() {
144-
match e.raw_os_error() {
145-
// these are non-fatal errors
146-
Some(libc::EBUSY | libc::ENOBUFS | libc::EAGAIN) => {}
147-
// this can temporarily happen with some drivers when changing
148-
// settings (eg with ethtool)
149-
Some(libc::ENETDOWN) => {
150-
log::warn!("network interface is down")
151-
}
152-
// we should never get here, hopefully the driver recovers?
153-
_ => {
154-
log::error!("network interface driver error: {e:?}");
155-
}
156-
}
157-
}
158-
};
159-
160136
let mut timeouts = 0;
161137
loop {
162138
match receiver.try_recv() {
@@ -193,25 +169,27 @@ pub fn tx_loop<T: AsRef<[u8]>, A: AsRef<[SocketAddr]>>(
193169

194170
for (addrs, payload) in batched_items.drain(..) {
195171
for addr in addrs.as_ref() {
196-
// loop until we have space for the next packet
197-
loop {
198-
completion.sync(true);
199-
// we haven't written any frames so we only need to sync the consumer position
200-
ring.sync(false);
201-
202-
// check if any frames were completed
203-
while let Some(frame_offset) = completion.read() {
204-
umem.release(frame_offset);
172+
if ring.available() == 0 || umem.available() == 0 {
173+
// loop until we have space for the next packet
174+
loop {
175+
completion.sync(true);
176+
// we haven't written any frames so we only need to sync the consumer position
177+
ring.sync(false);
178+
179+
// check if any frames were completed
180+
while let Some(frame_offset) = completion.read() {
181+
umem.release(frame_offset);
182+
}
183+
184+
if ring.available() > 0 && umem.available() > 0 {
185+
// we have space for the next packet, break out of the loop
186+
break;
187+
}
188+
189+
// queues are full, if NEEDS_WAKEUP is set kick the driver so hopefully it'll
190+
// complete some work
191+
kick(&ring);
205192
}
206-
207-
if ring.available() > 0 && umem.available() > 0 {
208-
// we have a frame and a slot in the ring
209-
break;
210-
}
211-
212-
// queues are full, if NEEDS_WAKEUP is set kick the driver so hopefully it'll
213-
// complete some work
214-
kick(&ring);
215193
}
216194

217195
// at this point we're guaranteed to have a frame to write the next packet into and
@@ -325,3 +303,33 @@ pub fn tx_loop<T: AsRef<[u8]>, A: AsRef<[SocketAddr]>>(
325303
kick(&ring);
326304
}
327305
}
306+
307+
// With some drivers, or always when we work in SKB mode, we need to explicitly kick the driver once
308+
// we want the NIC to do something.
309+
#[inline(always)]
310+
fn kick(ring: &TxRing<SliceUmemFrame<'_>>) {
311+
if !ring.needs_wakeup() {
312+
return;
313+
}
314+
315+
if let Err(e) = ring.wake() {
316+
kick_error(e);
317+
}
318+
}
319+
320+
#[inline(never)]
321+
fn kick_error(e: std::io::Error) {
322+
match e.raw_os_error() {
323+
// these are non-fatal errors
324+
Some(libc::EBUSY | libc::ENOBUFS | libc::EAGAIN) => {}
325+
// this can temporarily happen with some drivers when changing
326+
// settings (eg with ethtool)
327+
Some(libc::ENETDOWN) => {
328+
log::warn!("network interface is down")
329+
}
330+
// we should never get here, hopefully the driver recovers?
331+
_ => {
332+
log::error!("network interface driver error: {e:?}");
333+
}
334+
}
335+
}

0 commit comments

Comments
 (0)