dpdk · git:20260905.fd53e99 · 2026-09-05 · sha256 c9f3436f202a3494

dpdk git:20260905.fd53e99A

Immutable. This exact content is served forever at /api/v1/blob/c9f3436f202a3494.

---
name: dpdk
description: 'Use when initializing EAL, configuring PMD drivers and huge pages, using mbuf pools and rte_ring, setting up RSS, or validating a port with testpmd. Not for AF_XDP sockets: use af-xdp.'
---

# DPDK

## Contract

| Field | Bound contract |
|---|---|
| Trigger | Building a userspace DPDK dataplane: EAL init, port and PMD setup, `rte_eth_rx_burst`/`tx_burst`, mbuf pools, `rte_ring`, RSS, testpmd validation, or virtio-under-QEMU testing. |
| Authority | Read-only. Writes nothing. Chat output only. No remote mutation. |
| Side effect | Returns setup commands, driver code, and a validation plan. No source files are modified. |
| Done | The hugepage and EAL setup, the port init and burst loop, the RSS configuration, and a testpmd validation run are delivered. |

## Inputs

1. Target NIC and driver (required): the PCI address, the current kernel driver, and the PMD that will own it.
2. Core and memory layout (optional): lcore list, memory channels, hugepage size. Defaults to one lcore per RX queue.
3. Test plan (optional): testpmd forward mode and traffic generator, when the request includes validation.

## Procedure

1. Set up hugepages before EAL starts. EAL maps hugepages at startup and fails when pages are short.

   ```bash
   echo 1024 | sudo tee /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
   # 1 GiB pages help TLB reach on large-memory boxes
   echo 4 | sudo tee /sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages
   sudo mkdir -p /mnt/huge
   sudo mount -t hugetlbfs nodev /mnt/huge
   grep Huge /proc/meminfo
   ```

   Done when: `/proc/meminfo` reports the requested hugepage count.
2. Initialize EAL. `rte_eal_init` consumes its arguments and returns how many it took; the rest belong to the application.

   ```c
   int ret = rte_eal_init(argc, argv);
   if (ret < 0)
       rte_exit(EXIT_FAILURE, "EAL init failed\n");
   return run_dataplane(argc - ret, argv + ret);
   ```

   ```bash
   ./dpdk_app -l 0-3 -n 4 --huge-dir=/mnt/huge -- -p 0x3
   # -l: lcore list   -n: memory channels
   # --proc-type=primary   --file-prefix=pfx for a second instance
   ```

   Done when: the app starts with its lcore list and the argument split is respected.
3. Bind the NIC to a PMD and configure the port. Physical NICs need `vfio-pci` (with IOMMU) or a `uio` driver.

   ```bash
   sudo dpdk-devbind.py --status
   sudo modprobe vfio-pci
   sudo dpdk-devbind.py --bind=vfio-pci 0000:03:00.0
   ```

   ```c
   #define RX_RING_SIZE 1024
   #define TX_RING_SIZE 1024
   #define NUM_MBUFS    8191
   #define MBUF_CACHE   250

   mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS, MBUF_CACHE,
                                       0, RTE_MBUF_DEFAULT_BUF_SIZE,
                                       rte_socket_id());
   struct rte_eth_dev_info dev_info;
   rte_eth_dev_info_get(port, &dev_info);
   struct rte_eth_rxconf rxq_conf = dev_info.default_rxconf;
   rte_eth_rx_queue_setup(port, 0, RX_RING_SIZE,
                          rte_eth_dev_socket_id(port), &rxq_conf, mbuf_pool);
   rte_eth_tx_queue_setup(port, 0, TX_RING_SIZE,
                          rte_eth_dev_socket_id(port), &dev_info.default_txconf, mbuf_pool);
   rte_eth_dev_start(port);
   rte_eth_promiscuous_enable(port);
   ```

   Done when: the port starts and `dpdk-devbind.py --status` shows the PMD bound.
4. Write the burst loop. Poll mode has no interrupts; cores spin. Assign one core per RX queue.

   ```c
   struct rte_mbuf *bufs[BURST_SIZE];
   uint16_t nb_rx = rte_eth_rx_burst(port, 0, bufs, BURST_SIZE);
   if (nb_rx == 0)
       return;
   uint16_t nb_tx = rte_eth_tx_burst(port, 0, bufs, nb_rx);
   /* return unsent mbufs to the pool or they leak */
   for (uint16_t i = nb_tx; i < nb_rx; i++)
       rte_pktmbuf_free(bufs[i]);
   ```

   Done when: every received mbuf is either transmitted or freed.
5. Use `rte_ring` for inter-core handoff. Single-producer/single-consumer flags buy lock-free speed when the topology allows them.

   ```c
   struct rte_ring *ring = rte_ring_create("RX_RING", 1024, rte_socket_id(),
                                           RING_F_SP_ENQ | RING_F_SC_DEQ);
   rte_ring_enqueue_bulk(ring, (void **)bufs, nb_rx, NULL);
   uint16_t nb = rte_ring_dequeue_bulk(ring, (void **)bufs, BURST_SIZE, NULL);
   ```

   Done when: each ring has one producer and one consumer matching the flags.
6. Configure RSS when flows must spread over queues. Map queues to cores for parallelism.

   ```c
   static const struct rte_eth_rss_conf rss_conf = {
       .rss_key = NULL, /* default hash key */
       .rss_hf = RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP,
   };
   struct rte_eth_conf port_conf = {
       .rxmode = { .mq_mode = RTE_ETH_MQ_RX_RSS },
       .rx_adv_conf = { .rss_conf = rss_conf },
   };
   ```

   Done when: the receive-side table shows flows spread across the configured queues.
7. Validate with testpmd before custom code.

   ```bash
   ninja -C build dpdk-testpmd
   sudo ./build/app/dpdk-testpmd -l 0-3 -n 4 -- -i --forward-mode=io
   testpmd> show port stats all
   testpmd> start tx_first
   testpmd> show port stats all
   ```

   Forward modes `io`, `mac`, `macswap`, and `flowgen` serve throughput checks. Done when: both port counters advance at the expected rate.
8. For development under QEMU, present a multi-queue virtio device backed by hugepages.

   ```bash
   qemu-system-x86_64 -cpu host -m 4096 -smp 4 \
     -netdev user,id=net0 \
     -device virtio-net-pci,netdev=net0,mq=on,vectors=10 \
     -object memory-backend-file,id=mem,size=2G,mem-path=/dev/hugepages,share=on \
     -numa node,memdev=mem
   ```

   Inside the guest, bind the virtio PCI address to `vfio-pci`; `vhost-user` is the higher-throughput path. Route the host side: `qemu-kvm` for QEMU management. Done when: the guest sees the PMD and testpmd runs in it.
9. Pick the processing model from the per-packet work. Run-to-completion keeps RX, process, and TX on one core, which is the low-latency shape for simple logic. A pipeline (RX core, worker cores over rings, TX core) scales further for heavy per-packet work at the cost of ring hops. Done when: the recommendation names the per-packet work and the core budget.

## Failure and recovery

| Symptom | Cause | Recovery |
|---|---|---|
| EAL init fails on hugepages | Pages absent or hugetlbfs unmounted | Raise `nr_hugepages`; mount hugetlbfs. |
| `No probed ethernet devices` | NIC not bound to a PMD | `dpdk-devbind.py --bind=vfio-pci <addr>`. |
| RX drops climbing | Mbuf pool exhausted | Grow `NUM_MBUFS`; audit for unfreed mbufs. |
| 0 Mbps in testpmd | Port not started or link down | `start` in the testpmd shell; check link status. |
| VFIO permission error | No IOMMU group access | Enable IOMMU; fix group ownership. |
| Poor multi-core scaling | RSS off or too few queues | Enable RSS and add RX queues mapped to cores. |

## Output

Hugepage and EAL setup commands; port init code with the PMD binding; a burst loop that frees unsent mbufs; the ring and RSS configuration; a testpmd validation transcript; the QEMU development recipe; the processing-model choice.