How to Trace TCP Zero-Copy Receive and Memory Mapping Latency in Linux Using eBPF

While zero-copy network transmission (MSG_ZEROCOPY) is widely adopted for outbound streaming, high-throughput ingest pipelines—such as distributed message brokers, packet capture systems, and real-time database replicas—face severe CPU bottlenecks on inbound data. Standard network reads copy packet payloads from kernel network ring buffers into application memory. Linux TCP zero-copy receive eliminates this memory copy by allowing applications to map network packet buffers directly into user address space using mmap() and tcp_zerocopy_receive(). Using eBPF, systems engineers can trace zero-copy receive latencies, monitor page remapping overhead, and tune network queues.

Understanding TCP Zero-Copy Receive Architecture

Inbound zero-copy networking relies on hardware header-data splitting and memory remapping:

  • The network interface card (NIC) splits incoming frames into protocol headers and data payloads.
  • Data payloads are allocated into page-aligned page pool buffers.
  • The user application calls getsockopt(fd, SOL_TCP, TCP_ZEROCOPY_RECEIVE, &zc, sizeof(zc)), which remaps kernel pages directly into an address space previously allocated via mmap().
  • Trade-Off: Page remapping incurs TLB (Translation Lookaside Buffer) invalidations and kernel page table manipulation, which can outweigh copy savings on small payloads.

Tracing Zero-Copy Ingest with bpftrace

Deploy a real-time eBPF script targeting the kernel entry point tcp_zerocopy_receive():

# Install bpftrace on Ubuntu/Debian
sudo apt-get update && sudo apt-get install -y bpftrace linux-headers-$(uname -r)

# Trace TCP zero-copy receive invocations and remapped bytes
sudo bpftrace -e '
kprobe:tcp_zerocopy_receive {
    @start[tid] = nsecs;
}
kretprobe:tcp_zerocopy_receive /@start[tid]/ {
    $dur_us = (nsecs - @start[tid]) / 1000;
    @mmap_rx_lat_us = hist($dur_us);
    if (retval == 0) {
        @rx_success[comm] = count();
    } else {
        @rx_fallback[comm] = count();
    }
    delete(@start[tid]);
}'

If @rx_fallback is high, unaligned packet boundaries or lack of hardware header-data splitting is forcing the kernel into traditional copy paths.

Verifying Hardware and Socket Prerequisites

Inspect system-level zero-copy receive prerequisites:

# Check whether network interface supports header-data splitting
ethtool -k eth0 | grep -E "tcp-data-split|rx-gro"

# Enable header-data splitting on supported NICs
sudo ethtool -K eth0 tcp-data-split on

Tuning Inbound Page Mapping Buffers

To optimize zero-copy receive performance for production workloads:

  1. Payload Alignment: Ensure incoming TCP payloads align to page boundaries (4096 bytes or hugepages). Unaligned payloads fall back to copying.
  2. Batch Ingest Operations: Map multiple pages simultaneously per getsockopt() invocation to amortize TLB shootdown costs across megabyte-scale chunks.
  3. NIC Ring Sizing: Expand network device RX ring buffers (ethtool -G eth0 rx 4096) to prevent packet drops while page tables are modified.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.