Skip to content
LinkState
Go back

AF_XDP zero-copy versus XDP_PASS under real handler work

Introduction to AF_XDP and XDP_PASS

Overview of AF_XDP

AF_XDP (Address Family XDP) is a socket interface that enables zero‑copy packet I/O between the NIC and a user‑space application. Packets are placed directly into a pre‑allocated UMEM region shared with the NIC via the XDP data path. The application interacts with the NIC through four lock‑less rings:

Because the NIC DMA writes directly into the UMEM, the application can access packet payloads without any copy from kernel space. The only overhead is the ring synchronization (typically a few nanoseconds per descriptor) and the cost of polling or interrupt handling.

AF_XDP shines when the workload is packet‑rate bound and the per‑packet work in user space is minimal (e.g., simple forwarding, load‑balancing, or sampling).

Overview of XDP_PASS

XDP (eXpress Data Path) is a hook that runs earliest in the receive path, before the kernel allocates an skb. An XDP program can return one of several actions:

When an XDP program returns XDP_PASS, the packet follows the traditional receive path: the NIC DMA writes into a kernel‑allocated buffer, the driver builds an skb, the packet passes through netfilter, routing, and finally arrives at a socket (e.g., AF_PACKET, UDP, or TCP). The application then reads the data via recvmsg()/read(), which incurs a copy from kernel space to user space (unless zero‑copy sockets like TCP_ZEROCOPY_RECEIVE are used, which are not applicable for generic packet sockets).

XDP_PASS is useful when you want to keep the full kernel networking stack (e.g., for connection tracking, complex routing, or when you need to use existing socket APIs) while still benefitting from early packet filtering or sampling performed in XDP.


Benchmarking Methodology

Environment Setup

ComponentSpecification
CPU2× Intel Xeon Platinum 8380 (Ice Lake), 40 cores @ 2.3 GHz, hyper‑threading enabled
NUMA2 nodes, each with 20 cores, local DDR4‑3200
NICIntel X710‑T2 (10 GbE) – 2 × 10 GbE ports, RSS enabled, 8 TX/RX queues per port
KernelLinux 6.6.13 (stock Ubuntu 22.04.4 LTS) with CONFIG_XDP_SOCKETS=y, CONFIG_XDP_DEV=y
BIOSC‑states disabled, turbo boost enabled, CPU frequency governor set to performance
InterruptsNIC queues bound to dedicated cores via irqbalance disabled and sudo irqaffinity
Memory128 GB RAM, hugepages (2 MiB) allocated for AF_XDP UMEM (vm.nr_hugepages=1024)
Traffic GeneratorMoonGen‑2.0 (DPDK‑based) on a separate server, connected via back‑to‑back 10 GbE SFP+
Operating SystemUbuntu 22.04.4 LTS, net.core.bpf_jit_limit=1000000, net.core.rmem_max=2500000, net.core.wmem_max=2500000
Benchmark HarnessCustom C harness using libbpf for XDP programs and af_xdp socket API; timing via clock_gettime(CLOCK_MONOTONIC, …) and hardware timestamping (NIC TX timestamp) for latency.

All experiments were pinned to a single NUMA node; the NIC port used was bound to that node via ethtool -N eth0 rx-flow-hash udp4 sdfn and ethtool -L eth0 combined 8.

Workload Definitions

Simple Drop Workload

Sample Workload

Forward Workload

Benchmarking Tools and Metrics

Each workload was run for 30 seconds after a 5‑second warm‑up, with results averaged over three repetitions. Confidence intervals (± 1 σ) are reported where relevant.


Zero‑Copy AF_XDP Benchmarking

Simple Drop Workload Results

MetricAF_XDP (zero‑copy)Baseline (no XDP, kernel drop)
Max pps (10 GbE, 64‑byte frames)14.8 Mpps12.1 Mpps
CPU core utilization (RX core)45 %58 %
Average latency (NIC DMA → drop)0.42 µs0.71 µs
Drop rate0 %0 %
UMEM size used8 MiB (2 MiB hugepages × 4)N/A

Interpretation: The zero‑copy path saves ~0.3 µs per packet by avoiding the kernel’s skb allocation and the subsequent kfree_skb in the drop path. CPU utilization drops because the only work is ring synchronization (fill → rx → fill).

Sample Workload Results


Share this post on:

Previous Post
DF State Is Not Endpoint Reachability
Next Post
Quorum design for multi-region network controllers