embedded-deck
TEKNİK REHBER LİNUX PERFORMANS NETWORK DPDK 2026

DPDK
Wire-speed paket işleme, kernel olmadan.

Data Plane Development Kit ile Linux network stack'ini atlayın, milyon paket/saniye hızlarında L2/L3 işleme yapın — NFV, telecom ve edge uygulamaları için temel altyapı.

00 DPDK nedir?

Data Plane Development Kit (DPDK), Intel tarafından 2010'da başlatılmış ve 2017'de Linux Foundation'a devredilmiş açık kaynak bir kütüphane ve sürücü koleksiyonudur. Temel amacı, ağ paketlerini Linux kernel network stack'ini tamamen atlayarak (kernel bypass) doğrudan kullanıcı alanından işlemektir.

Neden kernel bypass?

Geleneksel Linux network stack'inde bir paketin serüveni şöyledir: NIC kesmesi → kernel IRQ handler → socket buffer kopyalama → sistem çağrısı geçişi → kullanıcı alanı. Bu yol her paket için yüzlerce CPU döngüsü tüketir, context switch ve bellek kopyalama ek yük getirir. 10GbE hattında ~14.8 milyon paket/saniye hedeflendiğinde bu yük kabul edilemez hale gelir.

  Geleneksel Linux:
  NIC → Kernel Driver → SKB Alloc → TCP/IP Stack → Socket → Memcpy → Uygulama
       [kesme]        [kopyalama]  [işleme]       [syscall] [kopyalama]

  DPDK:
  NIC → PMD (user-space poll) → rte_mbuf → Uygulama
       [polling, sıfır kopya]   [DMA, zero-copy]
    

Kullanım alanları

NFV (Network Function Virtualization)vRouter, vFirewall, vLoadBalancer — telecom operatörleri için sanal ağ fonksiyonları
Telecom / 5GgNodeB baseband işleme, fronthaul/midhaul paket yönetimi, O-RAN DU/CU
Edge ComputingMEC (Multi-access Edge Computing) uygulamaları, düşük gecikmeli paket yönlendirme
DPI (Deep Packet Inspection)İçerik tabanlı filtreleme, güvenlik duvarı, IDS/IPS sistemleri
Yük DengelemeL4/L7 yük dengeleme — nginx/HAProxy'ye göre 10-100x daha yüksek PPS

01 Mimari: EAL, lcore, hugepage, PMD

DPDK'nın temel mimarisi birkaç temel bileşenden oluşur. Her bileşen belirli bir sorunu çözmek için tasarlanmıştır.

EAL — Environment Abstraction Layer

EAL, DPDK uygulamalarının çalıştığı temel çerçevedir. CPU affinity, bellek yönetimi, PCI cihaz erişimi ve loglama gibi işletim sistemi bağımlı işlemleri soyutlar. Her DPDK uygulaması rte_eal_init() çağrısıyla EAL'ı başlatmak zorundadır.

  ┌────────────────────────────────────────────────────┐
  │                DPDK Uygulama                        │
  ├────────────────────────────────────────────────────┤
  │  rte_ethdev  rte_mbuf  rte_ring  rte_mempool       │
  │  (Ethernet)  (buffer)  (queue)   (memory)           │
  ├────────────────────────────────────────────────────┤
  │         EAL — Environment Abstraction Layer         │
  │   lcore mgmt │ hugepage alloc │ PCI probe           │
  ├───────────────┬──────────────────────────────────-──┤
  │  PMD (igb)    │  PMD (mlx5)   │  PMD (virtio)       │
  │  user-space   │  user-space   │  user-space          │
  │  NIC driver   │  NIC driver   │  NIC driver          │
  └───────────────┴───────────────┴────────────────────-┘
    

lcore — Logical Core

DPDK, ağ işleme iş yükünü belirli CPU çekirdeklerine (lcore) sabitler. Bu çekirdekler polling modunda çalışır; kesme bekleme yerine sürekli RX kuyruğunu kontrol eder. taskset veya EAL parametreleriyle belirlenen bu çekirdekler hiçbir zaman OS scheduler'a geri verilmemelidir.

# lcore 1 ve 2'yi DPDK'ya tahsis et, lcore 0'ı master olarak kullan:
./dpdk-app -l 0-2 -n 4

# CPU izolasyonu — kernel cmdline:
isolcpus=1,2 nohz_full=1,2 rcu_nocbs=1,2

Memory model ve hugepage

DPDK, 4KB standart sayfa boyutu yerine 2MB veya 1GB hugepage kullanır. Bu sayede TLB miss sayısı dramatik biçimde azalır. Büyük paket tamponları için TLB thrashing kritik bir darboğaz olabilir.

02 Hugepage kurulumu

DPDK çalışmaya başlamadan önce hugepage'ler tahsis edilmelidir. Bu tahsis sistem başlangıcında veya çalışma zamanında yapılabilir.

Çalışma zamanı hugepage tahsisi

# 2MB hugepage — mevcut durumu kontrol et
cat /proc/meminfo | grep -i huge
# HugePages_Total: 0
# HugePages_Free:  0
# Hugepagesize:    2048 kB

# 1024 adet 2MB hugepage tahsis et (= 2GB)
echo 1024 | sudo tee /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

# NUMA sistemlerde node bazında tahsis:
echo 512 | sudo tee /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
echo 512 | sudo tee /sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages

# 1GB hugepage (destekleniyorsa):
echo 4 | sudo tee /sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages

Kalıcı hugepage (kernel cmdline)

# /etc/default/grub içinde GRUB_CMDLINE_LINUX'a ekle:
GRUB_CMDLINE_LINUX="default_hugepagesz=2M hugepagesz=2M hugepages=2048 \
                    hugepagesz=1G hugepages=4 \
                    isolcpus=1-7 nohz_full=1-7"

sudo update-grub
sudo reboot

Hugepage dosya sistemi mount

# hugetlbfs mount — DPDK bunu otomatik yapar ama elle de yapılabilir:
sudo mkdir -p /dev/hugepages
sudo mount -t hugetlbfs hugetlbfs /dev/hugepages

# 1GB hugepage için ayrı mount point:
sudo mkdir -p /dev/hugepages1G
sudo mount -t hugetlbfs -o pagesize=1G hugetlbfs /dev/hugepages1G

# /etc/fstab'a ekle:
echo "hugetlbfs /dev/hugepages hugetlbfs defaults 0 0" | sudo tee -a /etc/fstab

dpdk-hugepages.py yardımcı scripti

# DPDK kurulumunda gelir:
sudo dpdk-hugepages.py --setup 2G         # 2GB 2M hugepage
sudo dpdk-hugepages.py --show             # mevcut durumu göster
sudo dpdk-hugepages.py --reserve 1G --pagesize 1G  # 1G hugepage
NOT

Hugepage tahsisi sistem çalışırken yapıldığında bellek fragmantasyonu nedeniyle başarısız olabilir. Kritik prodüksiyon sistemleri için hugepage tahsisini boot time'da hugepages= kernel parametresiyle yapın. NUMA sistemlerde her node'a eşit dağıtım yapın.

03 PMD sürücüler

Poll Mode Driver (PMD), donanıma özgü NIC erişimini sağlayan kullanıcı alanı sürücüsüdür. Kernel sürücüsü devre dışı bırakılır, NIC doğrudan kullanıcı alanına bağlanır.

Temel PMD'ler ve özellikleri

PMDNICMekanizmaÖzellik
igb / e1000Intel 1GbEUIO/VFIOGeliştirme ve test
ixgbeIntel 10GbE X540/X550UIO/VFIOProdüksiyon, SR-IOV
i40eIntel XL710/X710 40GbEVFIOPF/VF, Flow Director
iceIntel E810 100GbEVFIOEn yeni Intel NIC
mlx5Mellanox/NVIDIA 25/100GbEVerbs (libibverbs)Kernel sürücü ile birlikte çalışır
virtioKVM virtio-netVirtio specSanallaştırma, QEMU/KVM
vfio-pciTüm VFIO uyumluVFIO-PCIIOMMU koruması, önerilen
af_xdpKernel AF_XDP socketAF_XDPKernel sürücü tutulur, kısmi bypass

vfio-pci ile NIC bağlama

# 1. Mevcut NIC'i tespit et
lspci | grep Ethernet
# 0000:03:00.0 Ethernet controller: Intel Corporation 82599ES 10-Gigabit

# 2. Kernel sürücüyü kaldır
sudo modprobe vfio-pci
PCI_ADDR="0000:03:00.0"
sudo dpdk-devbind.py --status
sudo dpdk-devbind.py --bind=vfio-pci $PCI_ADDR

# 3. IOMMU etkinleştirme (kernel cmdline):
# Intel: intel_iommu=on iommu=pt
# AMD:   amd_iommu=on iommu=pt

# 4. Bağlamayı doğrula
sudo dpdk-devbind.py --status | grep vfio

mlx5 özel durum

# Mellanox mlx5 için özel durum: kernel sürücü kalmaya devam eder
# OFED veya inbox mlx5_core sürücüsü gerekir
sudo apt install rdma-core libibverbs-dev libmlx5-dev

# Bağlama GEREKMEZ — mlx5 PMD kernel sürücüyle konuşur:
sudo dpdk-devbind.py --status
# şu şekilde görünür: drv=mlx5_core (kullanılabilir: mlx5)

04 rte_mbuf — paket tamponu

rte_mbuf, DPDK'nın temel paket tamponu yapısıdır. Her ağ paketi bir rte_mbuf'ta saklanır. Sıfır kopya (zero-copy) tasarımı ile donanım DMA'sından doğrudan kullanıcı alanı işlemine gider.

rte_mbuf yapısı

  ┌─────────────────────────────────────────────────────────┐
  │                  rte_mbuf metadata                       │
  │  next│pool│buf_addr│data_off│pkt_len│data_len│ol_flags  │
  ├─────────────────────────────────────────────────────────┤
  │  headroom (RTE_PKTMBUF_HEADROOM = 128B)                 │
  ├─────────────────────────────────────────────────────────┤
  │  data_off                                               │
  │  ┌───────────────────────────────────────────────┐      │
  │  │  Ethernet Header │ IP Header │ TCP │ Payload  │      │
  │  └───────────────────────────────────────────────┘      │
  │  ← data_len →                                           │
  ├─────────────────────────────────────────────────────────┤
  │  tailroom                                               │
  └─────────────────────────────────────────────────────────┘
  Total buf_len = RTE_MBUF_DEFAULT_BUF_SIZE (2048B)
    

mempool oluşturma

#include <rte_mbuf.h>
#include <rte_mempool.h>

#define NUM_MBUFS        8191       /* 2^13 - 1, Mersenne sayısı önerilir */
#define MBUF_CACHE_SIZE  250
#define MBUF_DATA_SIZE   RTE_MBUF_DEFAULT_BUF_SIZE  /* 2048 */

struct rte_mempool *mbuf_pool;

mbuf_pool = rte_pktmbuf_pool_create(
    "MBUF_POOL",          /* isim */
    NUM_MBUFS,            /* eleman sayısı */
    MBUF_CACHE_SIZE,      /* per-lcore cache */
    0,                    /* private data boyutu */
    MBUF_DATA_SIZE,       /* veri alan boyutu */
    rte_socket_id()       /* NUMA node */
);
if (mbuf_pool == NULL)
    rte_exit(EXIT_FAILURE, "mempool oluşturulamadı\n");

mbuf erişim makroları

struct rte_mbuf *m;

/* Veri pointer'ı */
void *data = rte_pktmbuf_mtod(m, void *);

/* Belirli offset'ten cast */
struct rte_ether_hdr *eth =
    rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
struct rte_ipv4_hdr *ip =
    rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *,
                             sizeof(struct rte_ether_hdr));

/* Paket uzunluğu */
uint16_t pkt_len = rte_pktmbuf_pkt_len(m);

/* Offload flag'leri (checksum, VLAN, tunnel) */
uint64_t flags = m->ol_flags;
if (flags & RTE_MBUF_F_RX_IP_CKSUM_BAD)
    /* IP checksum hatalı */;

/* scatter-gather: zincirleme mbuf'lar */
struct rte_mbuf *seg = m;
while (seg != NULL) {
    /* her segmenti işle */
    seg = seg->next;
}

/* Serbest bırakma */
rte_pktmbuf_free(m);

05 rte_ring — kilitsiz kuyruk

rte_ring, DPDK'nın lock-free (kilitsiz) FIFO kuyruk implementasyonudur. lcore'lar arası mbuf pointer aktarımında kullanılır. CAS (Compare-and-Swap) atomic operasyonlarıyla thread-safe çalışır.

Ring türleri

SPSCSingle Producer Single Consumer — en yüksek performans, tek üretici tek tüketici
SPMCSingle Producer Multi Consumer — bir üretici, çoklu tüketici (work dispatch)
MPSCMulti Producer Single Consumer — çoklu üretici, tek tüketici (aggregation)
MPMCMulti Producer Multi Consumer — varsayılan, en genel durum
#include <rte_ring.h>

/* Ring oluşturma */
struct rte_ring *ring;
ring = rte_ring_create(
    "PKT_RING",           /* isim */
    1024,                 /* boyut — 2^N olmalı */
    rte_socket_id(),      /* NUMA socket */
    RING_F_SP_ENQ |       /* Single Producer */
    RING_F_SC_DEQ         /* Single Consumer */
);

/* Enqueue (paket gönder) */
struct rte_mbuf *m;
int ret = rte_ring_enqueue(ring, (void *)m);
if (ret == -ENOBUFS)
    /* ring dolu — paketi at */
    rte_pktmbuf_free(m);

/* Bulk enqueue — daha verimli */
struct rte_mbuf *burst[32];
unsigned int sent = rte_ring_enqueue_burst(ring,
    (void **)burst, 32, NULL);

/* Dequeue */
struct rte_mbuf *rx_mbuf;
ret = rte_ring_dequeue(ring, (void **)&rx_mbuf);

/* Bulk dequeue */
unsigned int rcvd = rte_ring_dequeue_burst(ring,
    (void **)burst, 32, NULL);

/* Doluluk kontrolü */
unsigned count = rte_ring_count(ring);
unsigned free  = rte_ring_free_count(ring);
NOT

Ring boyutu her zaman 2'nin kuvveti olmalıdır. Modulo operasyonu yerine bitmask kullanımı (count & (size-1)) performansı artırır. NUMA sistemlerde ring ve onu kullanan lcore'lar aynı NUMA node'unda olmalıdır; aksi takdirde remote memory access gecikmesi oluşur.

06 Temel DPDK uygulaması

Minimal bir DPDK uygulamasının iskelet kodu üç aşamadan oluşur: EAL başlatma, port kurulumu ve ana paket döngüsü.

EAL başlatma ve port kurulumu

#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_mbuf.h>

#define RX_RING_SIZE  1024
#define TX_RING_SIZE  1024
#define BURST_SIZE    32

static const struct rte_eth_conf port_conf = {
    .rxmode = {
        .mq_mode = RTE_ETH_MQ_RX_RSS,  /* RSS ile çoklu kuyruk */
    },
    .txmode = {
        .mq_mode = RTE_ETH_MQ_TX_NONE,
    },
    .rx_adv_conf.rss_conf = {
        .rss_key = NULL,
        .rss_hf = RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP,
    },
};

int port_init(uint16_t port, struct rte_mempool *mbuf_pool) {
    struct rte_eth_dev_info dev_info;
    int ret;

    if (!rte_eth_dev_is_valid_port(port))
        return -1;

    rte_eth_dev_info_get(port, &dev_info);

    /* Port yapılandırması */
    ret = rte_eth_dev_configure(port, 1, 1, &port_conf);
    if (ret != 0) return ret;

    /* MTU ayarla */
    rte_eth_dev_set_mtu(port, 9000);  /* Jumbo frame */

    /* RX queue kur */
    ret = rte_eth_rx_queue_setup(port, 0, RX_RING_SIZE,
        rte_eth_dev_socket_id(port), NULL, mbuf_pool);
    if (ret < 0) return ret;

    /* TX queue kur */
    ret = rte_eth_tx_queue_setup(port, 0, TX_RING_SIZE,
        rte_eth_dev_socket_id(port), NULL);
    if (ret < 0) return ret;

    /* Port başlat */
    ret = rte_eth_dev_start(port);
    if (ret < 0) return ret;

    /* Promiscuous mode */
    rte_eth_promiscuous_enable(port);
    return 0;
}

Ana paket döngüsü (polling loop)

static int lcore_main(void *arg) {
    uint16_t port;
    struct rte_mbuf *bufs[BURST_SIZE];
    uint16_t nb_rx, nb_tx;

    printf("lcore %u başlıyor\n", rte_lcore_id());

    RTE_ETH_FOREACH_DEV(port) {
        if (rte_eth_dev_socket_id(port) >= 0 &&
            rte_eth_dev_socket_id(port) != (int)rte_socket_id())
            printf("UYARI: port %u uzak NUMA node'unda\n", port);
    }

    /* Sonsuz polling döngüsü */
    for (;;) {
        RTE_ETH_FOREACH_DEV(port) {
            /* RX burst — BURST_SIZE'a kadar paket al */
            nb_rx = rte_eth_rx_burst(port, 0, bufs, BURST_SIZE);
            if (nb_rx == 0) continue;

            /* Paketleri işle (bu örnekte basitçe ilet) */
            uint16_t out_port = port ^ 1;  /* 0→1, 1→0 */

            /* TX burst */
            nb_tx = rte_eth_tx_burst(out_port, 0, bufs, nb_rx);

            /* İletilemeyen paketleri serbest bırak */
            if (unlikely(nb_tx < nb_rx)) {
                for (uint16_t i = nb_tx; i < nb_rx; i++)
                    rte_pktmbuf_free(bufs[i]);
            }
        }
    }
    return 0;
}

int main(int argc, char *argv[]) {
    struct rte_mempool *mbuf_pool;
    uint16_t nb_ports, portid;

    /* EAL başlat */
    int ret = rte_eal_init(argc, argv);
    if (ret < 0) rte_exit(EXIT_FAILURE, "EAL başlatılamadı\n");

    nb_ports = rte_eth_dev_count_avail();
    if (nb_ports < 2 || (nb_ports & 1))
        rte_exit(EXIT_FAILURE, "Çift sayıda port gerekli\n");

    mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL",
        NUM_MBUFS * nb_ports, MBUF_CACHE_SIZE, 0,
        RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());

    /* Her port için başlatma */
    RTE_ETH_FOREACH_DEV(portid)
        port_init(portid, mbuf_pool);

    /* lcore'lara görev dağıt */
    rte_eal_mp_remote_launch(lcore_main, NULL, CALL_MAIN);
    rte_eal_mp_wait_lcore();

    rte_eal_cleanup();
    return 0;
}

07 Pipeline modeli

Basit burst modeline ek olarak DPDK, P4 benzeri bir pipeline modeli sunar. Bu modelde paket işleme adımları (port → table → action) birer pipeline bileşenine dönüştürülür.

Pipeline bileşenleri

PortPipeline'ın giriş/çıkış noktaları — ethdev, ring, source/sink
TableEşleşme tablosu — exact match, LPM (IPv4 yönlendirme), ACL, hash
ActionEşleşme sonrası yapılacak işlem — forward, drop, counter, meter
SWX PipelineDPDK 20.11+ softswitch pipeline — P4 programlanabilir veri düzlemi

Basit LPM tablosu örneği

#include <rte_lpm.h>

struct rte_lpm *lpm;
struct rte_lpm_config config = {
    .max_rules = 1 << 16,      /* 65536 kural */
    .number_tbl8s = 1 << 8,
    .flags = 0,
};

lpm = rte_lpm_create("IPV4_LPM", rte_socket_id(), &config);

/* Kural ekle: 10.0.0.0/8 → next_hop 1 */
uint32_t ip = RTE_IPV4(10, 0, 0, 0);
rte_lpm_add(lpm, ip, 8, 1);

/* 192.168.1.0/24 → next_hop 2 */
ip = RTE_IPV4(192, 168, 1, 0);
rte_lpm_add(lpm, ip, 24, 2);

/* Lookup */
uint32_t next_hop;
struct rte_ipv4_hdr *ipv4_hdr = /* ... */;
int rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
                         &next_hop);
if (rc == 0)
    /* next_hop port'una ilet */
else
    /* drop */;

08 Pratik: L2 forwarding uygulaması

Bu bölümde iki NIC portu arasında wire-speed L2 forwarding yapan tam bir DPDK uygulaması geliştirilecektir. QEMU virtio portlarıyla test edilebilir.

Makefile

APP = l2fwd

# DPDK kurulum yolu
RTE_SDK ?= /usr/local/share/dpdk
RTE_TARGET ?= x86_64-native-linux-gcc

CFLAGS += -O3 -march=native
LDFLAGS += -lrte_eal -lrte_mbuf -lrte_ethdev -lrte_mempool \
           -lrte_ring -lrte_kvargs -lrte_pci -lrte_bus_pci

# pkg-config kullanımı (DPDK 20.11+)
CFLAGS  += $(shell pkg-config --cflags libdpdk)
LDFLAGS += $(shell pkg-config --libs libdpdk)

$(APP): l2fwd.c
	gcc $(CFLAGS) -o $@ $< $(LDFLAGS)

QEMU ile test ortamı kurma

# iki virtio-net interface ile QEMU başlatma:
sudo qemu-system-x86_64 \
  -enable-kvm \
  -m 4G \
  -cpu host \
  -smp 4 \
  -mem-path /dev/hugepages \
  -mem-prealloc \
  -netdev tap,id=net0,ifname=tap0,script=no,downscript=no \
  -device virtio-net-pci,netdev=net0,id=nic0 \
  -netdev tap,id=net1,ifname=tap1,script=no,downscript=no \
  -device virtio-net-pci,netdev=net1,id=nic1 \
  -drive file=ubuntu.img,format=qcow2 \
  -nographic

L2 forwarding çalıştırma ve performans ölçümü

# Hugepage tahsis et
echo 1024 | sudo tee /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

# NIC'leri DPDK'ya bağla
sudo modprobe vfio-pci
sudo dpdk-devbind.py --bind=vfio-pci 0000:00:03.0 0000:00:04.0

# L2 forwarding başlat (lcore 0: master, lcore 1-2: packet processing)
sudo ./l2fwd -l 0-2 -n 4 -- -p 0x3 -T 1

# Çıktı:
# Port statistics ====================================
# Statistics for port 0 ------------------------------
# Packets sent:                        10000000
# Packets received:                    10000000
# Packets dropped:                            0
# Port statistics ====================================
# Aggregate statistics ==============================
# Total packets sent:                  20000000
# Total packets received:              20000000
# Total packets dropped:                       0

Performans tuning ipuçları

CPU izolasyonuisolcpus= + nohz_full= — timer interrupt'larını uzaklaştır, %100 polling yaptır
NUMA affinityNIC ve lcore aynı NUMA node'unda olmalı — numactl --cpubind=0 --membind=0
Burst sizeBURST_SIZE=32 genellikle optimal — düşük gecikme için 16, yüksek throughput için 64
TX offloadingchecksum offload aktifleştir — RTE_ETH_TX_OFFLOAD_IPV4_CKSUM CPU yükünü azaltır
Flow DirectorIntel NIC'lerde RSS + Flow Director ile paketleri doğru lcore'a yönlendir
NOT

DPDK uygulamaları root ayrıcalığı gerektirdiğinden prodüksiyon ortamlarında dikkatli olun. --user flag'i ile non-root kullanım mümkündür ancak VFIO-noIOMMU veya özel grup/capability ayarları gerektirir. Container ortamlarında --privileged yerine minimum gerekli capability'leri (CAP_SYS_ADMIN, CAP_NET_ADMIN) tercih edin.