Network Device 드라이버 (net_device)

Linux 네트워크 디바이스 드라이버를 고처리량 데이터 경로와 운영 안정성 관점에서 심층 정리합니다. net_device/net_device_ops 초기화, NAPI 기반 RX 폴링, TX 큐 관리와 BQL, MSI-X/IRQ affinity 최적화, checksum/TSO/GRO 등 오프로드 기능, XDP/AF_XDP 연계, ethtool 통계와 링크 상태 관리, 물리 NIC와 TUN/TAP 같은 가상 netdev 공통 모델, tracepoint/perf/bpftrace를 활용한 병목 분석까지 실전 네트워크 드라이버 개발에 필요한 핵심을 다룹니다.

전제 조건: 디바이스 드라이버Workqueue 문서를 먼저 읽으세요. 입출력 인터페이스 드라이버는 데이터 경로와 제어 경로를 동시에 다루므로 큐/버퍼/비동기 처리 경계를 먼저 구분해야 합니다.
일상 비유: 이 주제는 콜센터 접수와 처리 라인 분리와 비슷합니다. 요청 접수와 실제 처리를 분리해 병목을 줄이듯이, 드라이버도 IRQ·큐·작업 스레드를 역할별로 나눠야 안정적입니다.

핵심 요약

  • net_device — 인터페이스의 공통 상태와 콜백 진입점입니다.
  • net_device_ops — open/stop/xmit 등 데이터 경로 계약을 정의합니다.
  • NAPI — RX 인터럽트 폭풍을 줄이고 폴링 기반 처리량을 확보합니다.
  • BQL — TX 큐 지연(latency)과 버퍼블로트 리스크를 줄입니다.
  • 가상 netdev — TUN/TAP처럼 하드웨어 없이도 동일한 netdev 모델을 재사용합니다.

단계별 이해

  1. 수명주기 설계
    할당/등록/해제 순서를 먼저 확정합니다.
  2. RX/TX 콜백 구현
    ndo_start_xmit()와 NAPI poll 루프를 정확히 연결합니다.
  3. 운영 인터페이스 연결
    ethtool_ops, 통계, 링크 상태(phylink)를 연결합니다.
  4. 가상 netdev 확장
    TUN/TAP, veth, virtio-net과 공통 패턴을 통합해 이해합니다.
예제 읽기 가이드: 이 문서는 개념 중심 설명을 기본으로 하되, 운영 환경에서 바로 점검할 수 있는 실습 예제를 함께 제공합니다. 코드 주석에 개념 예시가 표시된 블록은 구조와 호출 계약 이해용이며, 실습 예제가 표시된 블록은 사용자 공간에서 실행/검증 절차를 바로 적용할 수 있도록 구성했습니다.

개요: net_device 드라이버의 역할

struct net_device 드라이버는 커널 네트워크 스택과 실제/가상 링크 계층 사이의 어댑터입니다. 유저스페이스 입장에서는 eth0, ens3, tap0 모두 동일한 netdev 인터페이스처럼 보이지만, 내부 구현은 물리 NIC/가상 디바이스에 따라 크게 달라집니다.

User Space socket / ip / tc Network Stack sk_buff / routing net_device Driver net_device_ops + napi_struct + ethtool_ops 물리 NIC + 가상 netdev 공통 모델 PCIe NIC DMA/Ring Virtual netdev TUN/TAP, veth
net_device 모델은 물리 NIC와 가상 인터페이스를 동일한 커널 데이터 경로에 연결합니다.

드라이버 수명주기와 필수 호출 순서

가장 흔한 실수는 등록/해제 순서를 뒤섞는 것입니다. 특히 NAPI, IRQ, queue start/stop 순서는 패킷 손실과 use-after-free를 바로 유발합니다.

/* 개념 예시: net_device 수명주기와 등록 순서 */
#include <linux/netdevice.h>
#include <linux/etherdevice.h>

struct my_priv {
    struct net_device *ndev;
    struct napi_struct napi;
    spinlock_t tx_lock;
    void __iomem *bar0;
    int irq;
};

static int my_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
    struct net_device *ndev;
    struct my_priv *priv;
    int ret;

    ndev = alloc_etherdev_mqs(sizeof(*priv), 8, 8);
    if (!ndev)
        return -ENOMEM;

    priv = netdev_priv(ndev);
    priv->ndev = ndev;
    spin_lock_init(&priv->tx_lock);

    netif_napi_add(ndev, &priv->napi, my_napi_poll);
    ndev->netdev_ops = &my_netdev_ops;
    ndev->ethtool_ops = &my_ethtool_ops;

    ret = register_netdev(ndev);
    if (ret) {
        netif_napi_del(&priv->napi);
        free_netdev(ndev);
        return ret;
    }

    return 0;
}

static void my_remove(struct pci_dev *pdev)
{
    struct net_device *ndev = pci_get_drvdata(pdev);
    struct my_priv *priv = netdev_priv(ndev);

    unregister_netdev(ndev);
    netif_napi_del(&priv->napi);
    free_netdev(ndev);
}
실수 방지: free_netdev()는 반드시 unregister_netdev() 이후에 호출하세요. 등록된 netdev를 먼저 해제하면 notifier/RCU 경로에서 즉시 use-after-free가 발생할 수 있습니다.

핵심 콜백: net_device_ops 계약

net_device_ops는 드라이버와 코어 네트워크 스택 간의 ABI 역할을 합니다. 모든 콜백을 구현할 필요는 없지만, open/stop/xmit/statistics의 책임 분리는 명확해야 합니다.

콜백호출 시점핵심 책임
ndo_openip link set upIRQ/NAPI 활성화, RX/TX queue 시작
ndo_stopip link set downqueue 정지, IRQ 비활성화, NAPI 비활성화
ndo_start_xmit송신 경로skb를 TX ring에 게시하고 doorbell 트리거
ndo_get_stats64통계 조회race-safe한 64-bit 통계 제공
ndo_set_featuresoffload 변경TSO/GRO checksum offload 토글 처리
/* 개념 예시: open/stop에서 NAPI-IRQ 순서 보장 */
static int my_ndo_open(struct net_device *ndev)
{
    struct my_priv *priv = netdev_priv(ndev);

    my_hw_rx_ring_init(priv);
    my_hw_tx_ring_init(priv);

    napi_enable(&priv->napi);
    my_enable_irq(priv);
    netif_tx_start_all_queues(ndev);
    return 0;
}

static int my_ndo_stop(struct net_device *ndev)
{
    struct my_priv *priv = netdev_priv(ndev);

    netif_tx_disable(ndev);
    my_disable_irq(priv);
    napi_disable(&priv->napi);
    my_hw_rx_ring_cleanup(priv);
    my_hw_tx_ring_cleanup(priv);
    return 0;
}

RX 경로: IRQ, NAPI, budget 처리

수신 경로는 인터럽트 기반 진입 후 NAPI poll로 전환하는 모델이 표준입니다. 드라이버는 budget를 존중하며 완료 시 napi_complete_done()를 호출해야 합니다.

/* 개념 예시: IRQ top-half와 NAPI poll 연계 */
static irqreturn_t my_irq_handler(int irq, void *data)
{
    struct my_priv *priv = data;

    my_mask_rx_irq(priv);
    napi_schedule_irqoff(&priv->napi);
    return IRQ_HANDLED;
}

static int my_napi_poll(struct napi_struct *napi, int budget)
{
    struct my_priv *priv = container_of(napi, struct my_priv, napi);
    int work_done = 0;

    while (work_done < budget) {
        struct sk_buff *skb = my_rx_one_skb(priv);

        if (!skb)
            break;

        skb->protocol = eth_type_trans(skb, priv->ndev);
        napi_gro_receive(napi, skb);
        work_done++;
    }

    if (work_done < budget) {
        napi_complete_done(napi, work_done);
        my_unmask_rx_irq(priv);
    }

    return work_done;
}
성능 포인트: GRO를 쓰는 드라이버는 napi_gro_receive() 경로와 page recycling 전략을 함께 설계해야 합니다. 고속 NIC에서는 RX ring refill 정책이 drop/jitter를 크게 좌우합니다.

RX 전체 파이프라인

아래 다이어그램은 물리 와이어 수신부터 애플리케이션 소켓 버퍼 도달까지의 전체 수신 경로를 보여줍니다. 각 단계에서 일어나는 핵심 동작에 주의하세요.

Wire 물리 매체 NIC RX Ring DMA → 호스트 메모리 RX Completion desc 상태 확인 IRQ Top-Half mask + napi_schedule NAPI Poll Loop softirq 컨텍스트 실행 eth_type_trans 프로토콜 식별 + skb 설정 GRO 집계 napi_gro_receive netif_receive_skb 프로토콜 핸들러 전달 Protocol Stack IP → TCP/UDP Socket Buffer sk→sk_receive_queue Application recv() / read() RX 경로 핵심 포인트 ① IRQ top-half에서는 인터럽트 마스크 + napi_schedule만 수행 (최소 지연) ② NAPI poll에서 budget 미소진 시 napi_complete_done → IRQ 재활성화 ③ GRO는 동일 flow의 연속 패킷을 하나의 대형 skb로 집계하여 프로토콜 스택 부하 감소 ④ page pool / page recycling으로 RX ring refill 시 할당 오버헤드 최소화
RX 파이프라인 전체 흐름: 와이어 수신부터 애플리케이션 recv()까지의 전체 수신 경로입니다.

GRO (Generic Receive Offload) 동작 원리

GRO는 네트워크 스택 진입 전에 동일 flow의 연속 패킷들을 하나의 대형 sk_buff로 집계(merge)하여 프로토콜 스택 처리 횟수를 줄이는 소프트웨어 오프로드 기술입니다. 하드웨어 LRO(Large Receive Offload)와 달리 GRO는 프로토콜 레이어에서 stateless하게 동작하여 포워딩 환경에서도 안전하게 사용할 수 있습니다.

GRO 집계 과정은 다음과 같습니다:

  1. NAPI poll에서 napi_gro_receive() 호출 시, GRO 엔진은 napi→gro_hash[] 테이블에서 동일 flow를 검색합니다.
  2. 매칭되는 flow가 있으면 현재 패킷의 payload를 기존 skb의 frag_list 또는 frags[]에 병합합니다.
  3. flow가 완료되거나(PSH, FIN 등) 타이머/카운트 제한에 도달하면 집계된 대형 skb를 netif_receive_skb()로 전달합니다.
  4. 매칭되지 않으면 새 flow 엔트리를 생성하거나 즉시 전달합니다.
비교 항목LRO (Large Receive Offload)GRO (Generic Receive Offload)
구현 위치NIC 하드웨어 또는 드라이버커널 네트워크 스택 (dev_gro_receive)
상태 관리Stateful — TCP 헤더를 재작성Stateless — 원본 헤더 보존
포워딩 호환불가 — 재작성된 헤더로 인해 checksum/시퀀스 불일치가능 — GSO로 재분할 시 원본 복원
프로토콜 지원TCP만 (일반적)TCP, UDP, GRE, VXLAN 등 확장 가능
GSO 연계없음GRO → GSO 대칭 구조로 설계됨
커널 권장비권장 (ethtool -K eth0 lro off)기본 활성 (ethtool -K eth0 gro on)
/* GRO 수신 경로 핵심 흐름 (개념 예시) */
gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
{
    gro_result_t ret;

    skb_gro_reset_offset(skb);

    ret = dev_gro_receive(napi, skb);    /* flow 매칭 + 병합 시도 */

    switch (ret) {
    case GRO_MERGED:
    case GRO_MERGED_FREE:
        break;                              /* 병합 성공, skb 보관 */
    case GRO_HELD:
        break;                              /* 새 flow로 보관 */
    case GRO_NORMAL:
        gro_normal_one(napi, skb, 1);    /* GRO 불가, 즉시 전달 */
        break;
    case GRO_CONSUMED:
        break;
    }
    return ret;
}

/* NAPI poll 종료 시 GRO flush — 보관 중인 flow를 모두 전달 */
void napi_complete_done(struct napi_struct *napi, int work_done)
{
    gro_normal_list(napi);           /* 버퍼링된 GRO 패킷 일괄 전달 */
    /* ... napi state 전환, IRQ 재활성화 ... */
}
GRO가 성능을 저하시키는 경우: 소형 패킷 워크로드(DNS, VoIP, 게임 서버 등)에서는 GRO 집계 대기 시간이 오히려 latency를 증가시킬 수 있습니다. 또한 flow 수가 극도로 많아 GRO hash 충돌이 빈번한 환경에서는 CPU 오버헤드만 추가됩니다. 이런 경우 ethtool -K eth0 gro off를 고려하세요. 반대로, 벌크 TCP 전송(파일 서버, 스트리밍)에서는 GRO를 반드시 활성화해야 CPU 사용률이 크게 줄어듭니다.

NAPI 상태 머신

NAPI는 명확한 상태 전환 규약을 가진 상태 머신으로 동작합니다. 드라이버가 이 규약을 정확히 따르지 않으면 인터럽트 누수(IRQ 재활성화 누락)나 poll 미진입 같은 심각한 버그가 발생합니다.

IDLE SCHED softirq 대기 중 POLL softirq 컨텍스트 실행 DISABLED 드라이버 정지 napi_schedule() softirq 진입 napi_complete_done() budget 미소진 budget 소진 재스케줄 napi_disable() napi_disable() napi_enable()
NAPI 상태 머신: IDLE → SCHED → POLL → IDLE 순환과 DISABLED 전환 경로입니다.
상태 플래그비트의미
NAPI_STATE_SCHED0NAPI가 poll 목록에 스케줄됨. napi_schedule()가 설정, napi_complete_done()이 해제
NAPI_STATE_DISABLE1napi_disable() 진행 중. SCHED 비트 해제를 spin-wait
NAPI_STATE_NPSVC2busy polling 서비스 중 표시. poll이 non-softirq 컨텍스트에서 실행됨을 나타냄
NAPI_STATE_LISTED3NAPI가 디바이스의 napi_list에 등록됨 (netif_napi_add()가 설정)
NAPI_STATE_NO_BUSY_POLL4busy polling 비활성화 표시 (드라이버가 미지원 선언)
NAPI_STATE_IN_BUSY_POLL5현재 busy poll 실행 중 (re-entrant 방지)
NAPI_STATE_PREFER_BUSY_POLL6소켓이 busy poll 선호 표시 (SO_PREFER_BUSY_POLL)
NAPI_STATE_THREADED7threaded NAPI 모드 활성 — 전용 커널 스레드에서 poll 실행
NAPI_STATE_SCHED_THREADED8threaded NAPI에서 스케줄됨 표시
/* NAPI enable/disable 올바른 순서 (개념 예시) */

/* === ndo_open: 활성화 순서 === */
static int my_open(struct net_device *ndev)
{
    struct my_priv *priv = netdev_priv(ndev);

    /* 1. 하드웨어 초기화 (ring 할당, DMA 설정) */
    my_hw_init(priv);

    /* 2. NAPI 활성화 — 이 시점부터 napi_schedule 가능 */
    napi_enable(&priv->napi);

    /* 3. IRQ 등록 — NAPI enable 후에 해야 schedule이 동작 */
    request_irq(priv->irq, my_irq_handler, 0, "mynic", priv);

    /* 4. TX 큐 시작 */
    netif_tx_start_all_queues(ndev);

    return 0;
}

/* === ndo_stop: 비활성화 순서 === */
static int my_stop(struct net_device *ndev)
{
    struct my_priv *priv = netdev_priv(ndev);

    /* 1. TX 큐 정지 — 새 xmit 진입 차단 */
    netif_tx_disable(ndev);

    /* 2. IRQ 비활성화 — 새 napi_schedule 차단 */
    disable_irq(priv->irq);

    /* 3. napi_disable — 진행 중인 poll 완료를 대기 (barrier 역할) */
    napi_disable(&priv->napi);
    /* 이 시점 이후 poll 콜백이 절대 실행되지 않음을 보장 */

    /* 4. IRQ 해제 */
    free_irq(priv->irq, priv);

    /* 5. 하드웨어 정리 (ring 해제, DMA 해제) */
    my_hw_cleanup(priv);

    return 0;
}
napi_disable()의 barrier 의미: napi_disable()은 내부적으로 NAPI_STATE_SCHED 비트를 spin-wait하며, 진행 중인 poll이 napi_complete_done()을 호출해 SCHED를 해제할 때까지 대기합니다. 따라서 napi_disable() 반환 후에는 poll 콜백이 절대 실행되지 않음이 보장되며, 이후 ring 메모리를 안전하게 해제할 수 있습니다. 반드시 IRQ 비활성화 후, ring 해제 전에 호출해야 합니다.
NAPI threaded 모드 (커널 5.12+): echo 1 > /sys/class/net/eth0/threaded 또는 드라이버에서 dev_set_threaded(ndev, true)를 호출하면 NAPI poll이 softirq 대신 전용 커널 스레드(napi/eth0-N)에서 실행됩니다. 이 모드의 주요 장점:
  • RT 커널 호환: softirq는 PREEMPT_RT에서 스레드화되지만 우선순위 제어가 어렵습니다. threaded NAPI는 chrt로 직접 스케줄링 정책 설정 가능
  • CPU 격리: taskset으로 NAPI 스레드를 특정 코어에 바인딩하여 데이터플레인/컨트롤플레인 분리 가능
  • cgroup 통합: NAPI 스레드를 cgroup에 배치하여 CPU/메모리 자원 제한 가능
단, softirq 대비 컨텍스트 스위치 오버헤드가 추가되므로 초고속 NIC에서는 throughput이 약간 감소할 수 있습니다.

TX 경로: ndo_start_xmit, 큐 정지/재개, BQL

송신 경로의 핵심은 링 용량 관리입니다. TX ring이 포화됐을 때는 NETDEV_TX_BUSY를 남발하지 말고 queue stop/wake 모델을 일관되게 유지해야 합니다.

/* 개념 예시: 멀티큐 TX stop/wake + BQL 경로 */
static netdev_tx_t my_ndo_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
    struct my_priv *priv = netdev_priv(ndev);
    struct netdev_queue *txq = netdev_get_tx_queue(ndev, skb_get_queue_mapping(skb));
    unsigned long flags;

    spin_lock_irqsave(&priv->tx_lock, flags);

    if (my_tx_ring_avail(priv) < MAX_SKB_FRAGS + 2) {
        netif_tx_stop_queue(txq);
        spin_unlock_irqrestore(&priv->tx_lock, flags);
        return NETDEV_TX_BUSY;
    }

    my_map_skb_to_tx_desc(priv, skb);
    netdev_tx_sent_queue(txq, skb->len);
    my_ring_doorbell(priv);

    spin_unlock_irqrestore(&priv->tx_lock, flags);
    return NETDEV_TX_OK;
}

static void my_tx_complete(struct my_priv *priv, u16 qid)
{
    struct netdev_queue *txq = netdev_get_tx_queue(priv->ndev, qid);
    u32 bytes = 0, pkts = 0;

    my_reclaim_tx_desc(priv, &bytes, &pkts);
    netdev_tx_completed_queue(txq, pkts, bytes);

    if (netif_tx_queue_stopped(txq) && my_tx_ring_avail(priv) > 64)
        netif_tx_wake_queue(txq);
}

TX 전체 파이프라인

아래 다이어그램은 유저스페이스 send() 호출부터 물리 와이어 송출, TX 완료 인터럽트, 디스크립터 회수까지의 전체 송신 경로를 보여줍니다.

send() User Space sk_buff 생성 sock_alloc_send_skb Qdisc enqueue + dequeue dev_queue_xmit __dev_xmit_skb ndo_start_xmit 드라이버 진입점 TX Desc Ring desc 기록 + skb 연결 DMA Mapping dma_map_single/sg Doorbell Write MMIO → NIC에 알림 NIC Wire 송출 물리 매체 전송 TX Completion IRQ NIC → CPU 인터럽트 Desc Reclaim dma_unmap + kfree_skb BQL Feedback netdev_tx_completed_queue Queue Wake netif_tx_wake_queue 핵심 주의사항 ① DMA map 실패 시 skb를 free하고 ndev→stats.tx_dropped++ 처리 필수 ② queue stop과 doorbell 사이 race: stop → desc 확인 → 필요시 wake 패턴 권장 ③ BQL netdev_tx_sent_queue()는 doorbell 전에 호출해야 정확한 in-flight 바이트 추적 가능
TX 파이프라인 전체 흐름: 유저스페이스 send()부터 NIC 송출, 완료 인터럽트, 디스크립터 회수까지의 12단계 경로입니다.

BQL (Byte Queue Limits): 버퍼블로트 방지와 동적 큐 제한

BQL(Byte Queue Limits)은 커널 lib/dynamic_queue_limits.c에 구현된 동적 큐 깊이 제어 알고리즘입니다. NIC TX 큐에 쌓을 수 있는 바이트 수를 실시간으로 조정하여, 큐가 과도하게 깊어지는 버퍼블로트(bufferbloat)를 방지하면서도 충분한 처리량을 유지합니다. TX 경로 앞 절에서 netdev_tx_sent_queue/netdev_tx_completed_queue를 호출한 것이 바로 BQL API입니다. 이 절에서는 그 내부 알고리즘, 자료구조, sysfs 튜닝, 그리고 qdisc와의 상호작용을 깊이 있게 살펴봅니다.

버퍼블로트 문제와 BQL의 필요성

NIC의 TX ring이 크거나 드라이버가 큐 깊이를 제한하지 않으면, 상위 계층(qdisc, TCP 혼잡 제어)이 내린 결정과 무관하게 수백 ms에서 수 초 분량의 패킷이 하드웨어 큐에 쌓일 수 있습니다. 이 "버퍼블로트"는 지연(latency)을 극적으로 증가시키면서 처리량(throughput)은 거의 높이지 않습니다. BQL은 "지금 하드웨어에 내려보낸 바이트"와 "아직 완료되지 않은 바이트"를 추적하여 큐 깊이를 필요 최소한으로 동적 조절합니다.

큐 깊이 / 지연 시간 → 큐 깊이 (BQL 없음) 지연 (BQL 없음) 큐 깊이 (BQL 적용) 지연 (BQL 적용) BQL LIMIT (동적) 버퍼블로트 영역 — 실선: BQL 적용 --- 점선: BQL 미적용 BQL은 처리량 유지 + 지연 억제
BQL이 없으면 TX 큐 깊이가 무한히 증가하여 지연이 급등합니다. BQL은 동적 LIMIT으로 큐 깊이를 최소한으로 유지합니다.

BQL 동작 원리: 동적 한계 조정 알고리즘

BQL의 핵심은 lib/dynamic_queue_limits.c에 구현된 DQL(Dynamic Queue Limits) 알고리즘입니다. 드라이버가 패킷을 큐에 넣을 때(dql_queued)와 완료될 때(dql_completed)를 추적하여, 현재 inflight(미완료) 바이트LIMIT을 초과하면 큐를 멈추고, 완료 시 실제 사용 패턴에 따라 LIMIT을 올리거나 내립니다.

DQL 핵심 규칙:
  • LIMIT 감소 (오버슈트 교정): 완료 시점에 inflight가 LIMIT보다 큰 적이 없었으면(BELOW), LIMIT = inflight × (LIMIT / (LIMIT − ovlimit + slack)). 즉 과잉 분을 잘라냅니다.
  • LIMIT 증가 (여유 확보): 완료 시점에 inflight가 LIMIT 이상이었으면(ABOVE), 다음 주기에서 LIMIT이 부족한 것으로 보고 LIMIT += (completed − LIMIT) / 16 형태로 서서히 올립니다.
  • Slack: slack_hold_time(기본 HZ) 동안 관찰된 최소 여유분(slack)을 반영하여 불필요한 여유를 제거합니다.
BELOW inflight < LIMIT 유지 큐에 여유 있음 completed 시: LIMIT ↓ (과잉 제거) ABOVE inflight ≥ LIMIT 도달 큐 정지됨 completed 시: LIMIT ↑ (여유 확보) queued → inflight ≥ LIMIT completed → inflight < LIMIT LIMIT 조정 로직 (dql_completed 시) BELOW 경로: new_limit = LIMIT − (ovlimit − slack) → 축소 ABOVE 경로: new_limit = LIMIT + (completed − LIMIT)/16 → 확장 min_limit ≤ new_limit ≤ max_limit 범위 강제 (sysfs로 조정 가능)
DQL 알고리즘은 BELOW/ABOVE 두 상태 사이를 전이하며 LIMIT을 동적 조정합니다. 수렴 후에는 최소한의 큐 깊이만 유지합니다.
/* DQL 알고리즘 의사코드 (lib/dynamic_queue_limits.c 기반) */

/* ① 드라이버가 패킷을 큐에 넣을 때 */
void dql_queued(struct dql *dql, u32 count)
{
    dql->last_obj_cnt  = count;
    dql->num_queued   += count;

    /* inflight = num_queued - num_completed */
    if (inflight >= dql->adj_limit)
        netif_tx_stop_queue();   /* 큐 정지 — LIMIT 도달 */
}

/* ② TX 완료 인터럽트에서 */
void dql_completed(struct dql *dql, u32 count)
{
    dql->num_completed += count;
    ovlimit = dql->num_queued - dql->num_completed - dql->limit;

    if (ovlimit <= 0) {
        /* BELOW: inflight가 LIMIT 아래 — 과잉 제거 */
        dql->slack = min(dql->slack, ovlimit + dql->slack_start);
        if (slack_expired)
            new_limit = dql->limit - (ovlimit + dql->slack);
    } else {
        /* ABOVE: inflight가 LIMIT 이상 — LIMIT 확장 */
        new_limit = dql->limit + count / 16;  /* 완료분의 1/16 증가 */
    }
    dql->limit = clamp(new_limit, dql->min_limit, dql->max_limit);

    if (inflight < dql->adj_limit)
        netif_tx_wake_queue();   /* 큐 재개 */
}

핵심 자료구조: struct dql

BQL의 상태는 struct dql(include/linux/dynamic_queue_limits.h)에 저장됩니다. 각 TX 큐(struct netdev_queue)마다 하나의 dql 인스턴스가 내장되어 있습니다.

/* include/linux/dynamic_queue_limits.h */
struct dql {
    unsigned int  num_queued;       /* 큐에 넣은 누적 바이트 (단조 증가) */
    unsigned int  adj_limit;        /* 현재 유효 한계 (limit - num_completed) */
    unsigned int  last_obj_cnt;     /* 마지막 dql_queued 호출의 count */

    unsigned int  limit       ____cacheline_aligned_in_smp;
                                     /* 동적 LIMIT (바이트 단위) */
    unsigned int  num_completed;    /* 완료된 누적 바이트 (단조 증가) */

    unsigned int  prev_ovlimit;     /* 이전 주기의 오버리밋 값 */
    unsigned int  prev_num_queued;  /* 이전 주기의 num_queued */
    unsigned int  prev_last_obj_cnt;/* 이전 주기의 last_obj_cnt */

    unsigned int  lowest_slack;     /* 관찰된 최소 여유분 */
    unsigned long slack_start_time; /* slack 관찰 시작 시각 */

    unsigned int  max_limit;        /* sysfs 설정: LIMIT 상한 (기본 DQL_MAX_LIMIT) */
    unsigned int  min_limit;        /* sysfs 설정: LIMIT 하한 (기본 0) */
    unsigned int  slack_hold_time;  /* slack 관찰 윈도우 (기본 HZ=1초) */
};
캐시라인 분리: limitnum_completed는 TX 완료 경로(보통 softirq)에서 빈번히 갱신되므로 ____cacheline_aligned_in_smp로 분리하여 num_queued/adj_limit(송신 경로)와의 false sharing을 방지합니다.

드라이버 API 통합

드라이버가 BQL을 사용하려면 TX 경로의 세 지점에서 API를 호출합니다. 모든 API는 바이트 단위로 동작하며, 패킷 수가 아닌 누적 바이트를 전달해야 합니다.

① ndo_start_xmit skb를 TX ring에 매핑 DMA 디스크립터 작성 ② netdev_tx_sent_queue BQL에 전송 바이트 기록 → dql_queued(txq→dql, bytes) ③ doorbell kick NIC에 새 디스크립터 알림 wmb() + MMIO write ④ TX 완료 IRQ/NAPI 디스크립터 회수, bytes/pkts 집계 ⑤ netdev_tx_completed_queue BQL에 완료 바이트 기록 → dql_completed(txq→dql, bytes) → LIMIT 조정 netdev_tx_reset_queue 링크 다운/리셋 시 호출 BQL 카운터 초기화 NIC 처리 LIMIT 피드백 → 큐 wake/stop sent_queue()가 LIMIT 초과하면 큐 정지 → completed_queue()가 LIMIT 갱신 후 큐 재개
BQL API는 TX 송신(②)과 완료(⑤) 두 지점에서 호출되며, LIMIT 피드백으로 큐 stop/wake를 자동 제어합니다.
/* BQL 드라이버 API 3종 — 호출 시점과 인자 */

/* ① ndo_start_xmit() 내부, skb를 ring에 넣은 직후 */
netdev_tx_sent_queue(txq, skb->len);
/*  txq  : netdev_get_tx_queue(ndev, queue_index)
 *  bytes: 전송한 바이트 수 (skb->len)
 *  내부: dql_queued(&txq->dql, bytes)
 *  inflight ≥ limit이면 __netif_tx_stop_queue() 호출 */

/* ② TX 완료 인터럽트/NAPI에서, 디스크립터 회수 후 */
netdev_tx_completed_queue(txq, pkts, bytes);
/*  pkts : 완료된 패킷 수 (BQL 자체는 bytes만 사용)
 *  bytes: 완료된 바이트 수
 *  내부: dql_completed(&txq->dql, bytes)
 *  LIMIT 재조정 + 큐 wake 판단 */

/* ③ ndo_stop() 또는 링크 다운/리셋 시 */
netdev_tx_reset_queue(txq);
/*  모든 BQL 카운터 초기화 (num_queued, num_completed 등)
 *  인터페이스 down → up 사이클에서 반드시 호출
 *  빠뜨리면 stale 카운터로 큐가 영구 정지될 수 있음 */
흔한 실수: ndo_stop()에서 netdev_tx_reset_queue()를 빠뜨리면, 다음 ndo_open() 후 stale 카운터 때문에 BQL이 즉시 큐를 멈추고 트래픽이 흐르지 않습니다. 멀티큐 드라이버는 모든 TX 큐에 대해 개별 reset을 호출해야 합니다.

sysfs 인터페이스와 튜닝

각 TX 큐의 BQL 파라미터는 /sys/class/net/<dev>/queues/tx-<N>/byte_queue_limits/ 경로에 노출됩니다. 운영 환경에서 BQL 동작을 관찰하고 미세 조정할 수 있는 핵심 인터페이스입니다.

# BQL sysfs 파일 확인 (예: eth0의 tx-0 큐)
ls /sys/class/net/eth0/queues/tx-0/byte_queue_limits/
# 출력: hold_time  inflight  limit  limit_max  limit_min

# 현재 동적 LIMIT 확인 (알고리즘이 결정한 값)
cat /sys/class/net/eth0/queues/tx-0/byte_queue_limits/limit

# inflight 바이트 확인 (현재 NIC에서 처리 중인 양)
cat /sys/class/net/eth0/queues/tx-0/byte_queue_limits/inflight

# LIMIT 상한 조정 (기본값: DQL_MAX_LIMIT = 매우 큰 값)
# 지연에 민감한 워크로드에서 상한을 낮추면 지연이 더 줄어들 수 있음
echo 30000 > /sys/class/net/eth0/queues/tx-0/byte_queue_limits/limit_max

# LIMIT 하한 조정 (기본값: 0)
# 너무 낮은 LIMIT으로 인한 성능 저하 방지
echo 1500 > /sys/class/net/eth0/queues/tx-0/byte_queue_limits/limit_min

# slack 관찰 윈도우 조정 (기본값: 1000 = HZ, 즉 1초)
cat /sys/class/net/eth0/queues/tx-0/byte_queue_limits/hold_time

# 모든 큐의 BQL limit 한 번에 확인
for q in /sys/class/net/eth0/queues/tx-*/byte_queue_limits/limit; do
    echo "$(dirname $(dirname $q)): $(cat $q)"
done
sysfs 파일읽기/쓰기설명
limitR현재 동적 LIMIT (바이트). 알고리즘이 자동 조정
limit_maxR/WLIMIT 상한. 낮추면 최대 큐 깊이를 제한
limit_minR/WLIMIT 하한. 높이면 최소 처리량 보장
hold_timeR/Wslack 관찰 윈도우 (ms). 기본 1000
inflightR현재 미완료 바이트 (num_queued − num_completed)

BQL과 qdisc/TC의 상호작용

BQL은 qdisc 아래, NIC ring 위에 위치합니다. 패킷 흐름에서 BQL의 정확한 위치를 이해하면 fq_codel 같은 AQM(Active Queue Management)과의 시너지를 극대화할 수 있습니다.

Application (send/sendmsg) TCP/UDP → sk_buff 생성 qdisc (fq_codel, htb, pfifo_fast ...) 스케줄링 + AQM (ECN marking, drop) BQL (Byte Queue Limits) 동적 LIMIT으로 큐 stop/wake 제어 NIC TX Ring (DMA descriptors) Wire (물리 매체) 정책 결정 깊이 제어 하드웨어 fq_codel + BQL 시너지 BQL: 하드웨어 큐 제한 fq_codel: 소프트웨어 AQM → 전 구간 지연 최소화 → 공정한 대역폭 분배
BQL은 qdisc(소프트웨어 스케줄링)와 NIC ring(하드웨어 큐) 사이에서 동작합니다. fq_codel과 결합하면 소프트웨어+하드웨어 전 구간의 지연을 제어할 수 있습니다.
fq_codel + BQL 조합이 강력한 이유:
  • fq_codel은 qdisc 레벨에서 소프트웨어 큐의 지연을 제어합니다 (sojourn time 기반 drop/ECN).
  • BQL은 드라이버 레벨에서 하드웨어 큐에 과도한 바이트가 쌓이는 것을 방지합니다.
  • BQL 없이 fq_codel만 사용하면 NIC ring에 수백 패킷이 쌓여 fq_codel의 AQM 효과가 무력화됩니다.
  • BQL이 하드웨어 큐 깊이를 최소화하면, fq_codel이 더 정확한 sojourn time을 측정하여 공정한 스케줄링이 가능합니다.

실전 디버깅과 모니터링

BQL이 올바르게 동작하는지 확인하고, 문제 발생 시 원인을 추적하는 방법입니다.

# ── BQL 상태 종합 확인 ──
# 모든 TX 큐의 limit과 inflight를 한 번에 출력
for q in /sys/class/net/eth0/queues/tx-*/byte_queue_limits; do
    echo "=== $(basename $(dirname $q)) ==="
    echo "  limit:    $(cat $q/limit)"
    echo "  inflight: $(cat $q/inflight)"
    echo "  max:      $(cat $q/limit_max)"
    echo "  min:      $(cat $q/limit_min)"
done

# ── tc 통계와 BQL 연계 확인 ──
# qdisc의 backlog과 BQL의 inflight를 비교하여 병목 위치 판단
tc -s qdisc show dev eth0

# ── bpftrace로 BQL limit 변화 실시간 추적 ──
# dql_completed 호출 시 limit 값 변화를 추적
bpftrace -e 'kprobe:dql_completed {
    $dql = (struct dql *)arg0;
    printf("cpu=%d limit=%u completed=%u\n",
           cpu, $dql->limit, arg1);
}'

# ── perf로 BQL 관련 함수 호출 빈도 확인 ──
perf stat -e 'probe:dql_queued,probe:dql_completed' -a sleep 5

# ── 문제 진단 체크리스트 ──
# 1. limit이 0이면? → netdev_tx_reset_queue() 누락 가능
# 2. inflight가 limit과 같고 큐 정지? → 정상 (완료 대기 중)
# 3. limit이 limit_max에 고정? → 트래픽이 항상 LIMIT 소진 → limit_max 낮출 것
# 4. limit이 매우 작고 throughput 저하? → limit_min을 MTU 이상으로 설정
빠른 진단 공식: inflight ≈ limit이 지속되면 BQL이 적극적으로 큐를 제한하고 있다는 뜻입니다. 이때 throughput이 충분하면 정상이고, 부족하면 limit_min을 높이거나 NIC의 TX 완료 인터럽트 코얼레싱을 줄여 완료 통지를 빠르게 받아 LIMIT을 더 빨리 해제하세요.

LLTX (NETIF_F_LLTX): lockless TX 계약과 실무 주의점

NETIF_F_LLTX는 TX 잠금을 네트워크 코어가 아닌 드라이버가 직접 책임지는 오래된 모델입니다. 즉, ndo_start_xmit() 동시 호출에 대한 직렬화/경합 제어를 드라이버가 스스로 보장해야 하며, 큐 stop/wake, timeout 복구, completion 경로까지 하나의 동시성 계약으로 맞춰야 합니다.

핵심 경고: LLTX는 신규 드라이버의 기본 선택지가 아닙니다. 멀티큐 + queue별 락/BQL 모델이 이미 충분히 고성능이며, LLTX는 lock inversion, queue state 경합, watchdog 오탐(false timeout) 같은 장애 확률을 높입니다.
항목일반 TX 경로LLTX 경로
직렬화 주체코어/큐 락 + 드라이버 보조 락드라이버가 전적으로 책임
병목 위치락 경합은 비교적 예측 가능드라이버 구현 품질에 따라 편차 큼
디버깅 난이도표준 패턴과 도구가 많음race 재현/분석 난이도 높음
권장도신규 구현 권장기존 드라이버 유지보수 목적 외 비권장

LLTX를 유지해야 하는 코드베이스라면 아래 4가지를 반드시 고정 규칙으로 문서화해야 합니다.

  1. xmit 직렬화 규칙
    ndo_start_xmit()의 re-entry 허용 범위(전역/큐별)를 명시하고 락 순서를 고정
  2. queue 상태 전이 규칙
    netif_tx_stop_queue()/netif_tx_wake_queue() 호출 조건을 단일 함수로 중앙화
  3. completion 메모리 순서
    descriptor reclaim 이후 wake 판단 전까지의 barrier 규칙을 아키텍처별로 검증
  4. timeout 복구 규칙
    ndo_tx_timeout()에서 즉시 리셋하지 말고 workqueue로 이관해 중복 reset 방지
/* LLTX 유지보수 시 권장되는 최소 패턴 (개념 예시) */
static netdev_tx_t my_lltx_xmit(struct sk_buff *skb, struct net_device *ndev)
{
    struct my_priv *priv = netdev_priv(ndev);
    unsigned long flags;

    /* LLTX에서는 드라이버가 자체 직렬화를 반드시 보장 */
    spin_lock_irqsave(&priv->tx_lock, flags);

    if (!my_has_room(priv)) {
        my_stop_txq_if_needed(priv);
        spin_unlock_irqrestore(&priv->tx_lock, flags);
        return NETDEV_TX_BUSY;
    }

    my_post_desc(priv, skb);
    my_kick_doorbell(priv);
    spin_unlock_irqrestore(&priv->tx_lock, flags);
    return NETDEV_TX_OK;
}
마이그레이션 전략: LLTX 기반 구형 드라이버를 개선할 때는 한 번에 lockless를 유지하려 하지 말고, 먼저 queue별 락 + 명확한 stop/wake + BQL 계측을 적용한 뒤 성능/지연을 재측정하는 접근이 안전합니다.

통계와 ethtool 연동

운영 환경에서는 “성능이 안 나온다”보다 “왜 안 나오는가”를 보여주는 통계가 더 중요합니다. ethtool -S로 확인 가능한 드라이버 통계를 설계하면 장애 분석 시간이 크게 줄어듭니다.

/* 개념 예시: ethtool 통계 구조와 per-CPU 집계 */
struct my_pcpu_stats {
    u64 rx_packets;
    u64 rx_bytes;
    u64 tx_packets;
    u64 tx_bytes;
    struct u64_stats_sync syncp;
};

static void my_ndo_get_stats64(struct net_device *ndev,
                               struct rtnl_link_stats64 *stats)
{
    int cpu;

    for_each_possible_cpu(cpu) {
        struct my_pcpu_stats *pcpu = per_cpu_ptr(my_stats, cpu);
        u64 rx_pkts, rx_bytes, tx_pkts, tx_bytes;
        unsigned int start;

        do {
            start = u64_stats_fetch_begin(&pcpu->syncp);
            rx_pkts = pcpu->rx_packets;
            rx_bytes = pcpu->rx_bytes;
            tx_pkts = pcpu->tx_packets;
            tx_bytes = pcpu->tx_bytes;
        } while (u64_stats_fetch_retry(&pcpu->syncp, start));

        stats->rx_packets += rx_pkts;
        stats->rx_bytes += rx_bytes;
        stats->tx_packets += tx_pkts;
        stats->tx_bytes += tx_bytes;
    }
}
진단 명령확인 포인트
ethtool -i eth0드라이버/펌웨어 버전
ethtool -k eth0TSO/GRO/checksum offload 상태
ethtool -S eth0링 드롭, 에러, 큐별 카운터
ethtool -l eth0채널(RX/TX queue) 구성
ip -s link show dev eth0커널 링크 통계의 상위 뷰

현대 NIC/MAC 드라이버는 PHY 연결을 phylink로 통합하는 추세입니다. SFP, fixed-link, in-band status를 동시에 다뤄야 하는 경우 phylink가 사실상 표준입니다.

/* 개념 예시: phylink 초기화와 플랫폼별 연결 분기 */
static const struct phylink_mac_ops my_phylink_ops = {
    .mac_config = my_mac_config,
    .mac_link_up = my_mac_link_up,
    .mac_link_down = my_mac_link_down,
};

static int my_phylink_init(struct my_priv *priv)
{
    struct phylink_config *cfg = &priv->phylink_config;
    struct fwnode_handle *fwnode = dev_fwnode(priv->dev);
    phy_interface_t iface = priv->phy_mode; /* DT/ACPI 설정에서 파생 */

    priv->phylink = phylink_create(cfg, fwnode, iface,
                                 &my_phylink_ops);
    if (IS_ERR(priv->phylink))
        return PTR_ERR(priv->phylink);

    /* 펌웨어 타입(OF/fwnode)에 맞는 connect 경로를 선택 */
    if (is_of_node(fwnode))
        return phylink_of_phy_connect(priv->phylink, to_of_node(fwnode), 0);
    return phylink_fwnode_phy_connect(priv->phylink, fwnode, 0);
}

XDP, AF_XDP, 드라이버 오프로드

XDP 지원 드라이버는 RX hot path 초기에 프로그램을 실행해 drop/redirect를 빠르게 처리합니다. ndo_bpf, zero-copy AF_XDP, page_pool의 조합이 고성능 경로의 핵심입니다.

/* 개념 예시: XDP action 분기와 프레임 반환 계약 */
static int my_xdp_run(struct my_priv *priv, struct xdp_buff *xdp)
{
    u32 act;

    act = bpf_prog_run_xdp(rcu_dereference(priv->xdp_prog), xdp);

    switch (act) {
    case XDP_PASS:
        return XDP_PASS;
    case XDP_DROP:
        xdp_return_frame_rx_napi(xdp);
        return XDP_DROP;
    case XDP_TX:
        my_xdp_xmit(priv, xdp);
        return XDP_TX;
    default:
        xdp_return_frame_rx_napi(xdp);
        return XDP_ABORTED;
    }
}
연계 문서: XDP 프로그램 작성과 AF_XDP 유저스페이스 큐 모델은 BPF/XDP, AF_XDP 문서에서 이어서 확인하세요.

멀티큐, RSS, IRQ affinity 설계

10/25/100GbE 구간에서는 단일 큐 모델이 거의 항상 병목입니다. 드라이버는 RX/TX 큐, MSI-X vector, NAPI 인스턴스를 1:1 또는 N:1로 설계하고, NUMA/CPU 토폴로지에 맞춰 IRQ affinity를 배치해야 합니다.

/* 개념 예시: 멀티큐 qvec와 MSI-X 벡터 매핑 */
struct my_qvec {
    struct napi_struct napi;
    int qid;
    int irq;
};

static int my_alloc_qvecs(struct my_priv *priv, int num_q)
{
    int i;

    for (i = 0; i < num_q; i++) {
        struct my_qvec *qv = &priv->qvec[i];

        qv->qid = i;
        netif_napi_add(priv->ndev, &qv->napi, my_qvec_poll);
        my_request_msix_vector(priv, i, &qv->irq, my_msix_irq_handler);
    }

    return 0;
}
설정 항목실무 기준
큐 개수활성 CPU 수와 동일 또는 NUMA 노드 단위
RSS indirection핫플로우가 특정 큐에 치우치지 않게 분산
IRQ affinity해당 큐를 소비하는 CPU에 고정
RPS/RFSHW RSS 부족 시 보조적으로 사용

RX 메모리 경로: page_pool과 DMA recycling

고속 수신 경로에서 alloc_pages()/dma_map를 패킷마다 반복하면 CPU 비용이 폭증합니다. page_pool 기반 재사용은 대부분의 고성능 NIC 드라이버에서 사실상 표준 패턴입니다.

/* 개념 예시: page_pool 기반 RX 메모리 재사용 */
static int my_rx_pool_init(struct my_priv *priv)
{
    struct page_pool_params pp = {
        .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
        .order = 0,
        .pool_size = 4096,
        .nid = dev_to_node(priv->dev),
        .dev = priv->dev,
        .dma_dir = DMA_FROM_DEVICE,
    };

    priv->rx_pp = page_pool_create(&pp);
    if (IS_ERR(priv->rx_pp))
        return PTR_ERR(priv->rx_pp);

    return 0;
}

static void my_rx_recycle_page(struct my_priv *priv, struct page *page)
{
    page_pool_recycle_direct(priv->rx_pp, page);
}
주의: XDP + page_pool 조합에서는 frame ownership 규칙을 엄격히 지켜야 합니다. XDP_REDIRECT, XDP_TX, XDP_DROP 경로별 반환 API를 혼용하면 double free/메모리 누수가 쉽게 발생합니다.

제어 경로: RTNL, RTNETLINK, feature 토글

데이터 경로가 빠르더라도 제어 경로가 불안정하면 운영 장애가 반복됩니다. MTU 변경, queue 개수 변경, 링크 down/up, offload 토글은 모두 RTNL 보호 하에서 일관되게 처리해야 합니다.

/* 개념 예시: MTU 변경 시 stop/open 오류 경로 보강 */
static int my_ndo_change_mtu(struct net_device *ndev, int new_mtu)
{
    int ret;
    int old_mtu = ndev->mtu;

    if (new_mtu < 68 || new_mtu > 9700)
        return -EINVAL;

    if (netif_running(ndev)) {
        ret = my_ndo_stop(ndev);
        if (ret)
            return ret;

        ndev->mtu = new_mtu;
        ret = my_ndo_open(ndev);
        if (ret) {
            ndev->mtu = old_mtu;
            return ret;
        }
        return 0;
    }

    ndev->mtu = new_mtu;
    return 0;
}

static int my_ndo_set_features(struct net_device *ndev, netdev_features_t features)
{
    netdev_features_t changed = ndev->features ^ features;

    if (changed & NETIF_F_TSO)
        my_hw_toggle_tso(ndev, !!(features & NETIF_F_TSO));
    if (changed & NETIF_F_GRO)
        my_hw_toggle_gro(ndev, !!(features & NETIF_F_GRO));

    return 0;
}

TC/NFT 오프로드와 switchdev 연계

데이터센터 NIC는 tc flower 규칙을 하드웨어 테이블로 오프로드해 CPU 부하를 낮춥니다. 이때 드라이버는 수용 가능한 매치/액션 집합을 명확히 제한하고, 부분 실패 시 fallback 정책을 분명히 해야 합니다.

/* 개념 예시: TC setup type별 오프로드 분기 */
static int my_ndo_setup_tc(struct net_device *ndev, enum tc_setup_type type, void *type_data)
{
    switch (type) {
    case TC_SETUP_BLOCK:
        return my_tc_block_cb_setup(ndev, type_data);
    case TC_SETUP_QDISC_MQPRIO:
        return my_mqprio_setup(ndev, type_data);
    default:
        return -EOPNOTSUPP;
    }
}
오프로드 대상대표 인터페이스주의점
분류/필터tc flower + ndo_setup_tc규칙 우선순위/충돌 처리
eSwitchswitchdev, representor netdevVF/representor 일관성
암호화/터널xfrm offload, UDP tunnel offloadfallback 경로와 통계 구분

리셋/장애 복구: devlink health와 watchdog

실서비스에서는 드라이버의 평균 성능보다 복구 전략이 더 중요합니다. TX timeout, 펌웨어 hang, PCI AER 오류에서 자동 복구가 되지 않으면 장기 장애로 이어집니다.

/* 개념 예시: tx_timeout 복구를 workqueue로 분리 */
static void my_tx_timeout(struct net_device *ndev, unsigned int txqueue)
{
    struct my_priv *priv = netdev_priv(ndev);

    netdev_warn(ndev, "tx timeout on queue %u\\n", txqueue);
    schedule_work(&priv->reset_work);
}

static void my_reset_work(struct work_struct *work)
{
    struct my_priv *priv = container_of(work, struct my_priv, reset_work);

    rtnl_lock();
    my_ndo_stop(priv->ndev);
    my_hw_function_reset(priv);
    my_ndo_open(priv->ndev);
    rtnl_unlock();
}

오프로드 계약: checksum/GSO/GRO/VLAN

오프로드 기능은 “켜고 끄는 옵션”이 아니라 드라이버와 스택 사이의 계약입니다. advertise한 기능을 데이터 경로에서 일관되게 지키지 않으면 패킷 손실, checksum 오류, MTU 이상 동작이 발생합니다.

/* 개념 예시: feature dependency를 fix_features에서 강제 */
static netdev_features_t my_ndo_fix_features(struct net_device *ndev,
                                       netdev_features_t features)
{
    /* HW가 IPv6 TSO를 지원하지 않으면 강제 비활성화 */
    if (!(features & NETIF_F_IP_CSUM))
        features &= ~NETIF_F_TSO;

    if (!my_hw_supports_tso6(ndev))
        features &= ~NETIF_F_TSO6;

    return features;
}

static netdev_features_t my_ndo_features_check(struct sk_buff *skb,
                                            struct net_device *ndev,
                                            netdev_features_t features)
{
    /* 헤더 길이/세그먼트 조건 미충족 시 SW fallback */
    if (skb_is_gso(skb) && skb_shinfo(skb)->gso_segs > 512)
        features &= ~(NETIF_F_GSO_MASK);

    return features;
}
기능군관련 플래그/API드라이버 확인 포인트
Checksum offloadNETIF_F_HW_CSUM, skb->ip_summedpartial checksum descriptor 구성
TSO/GSONETIF_F_TSO*, gso_size세그먼트 제한, header split 처리
GRO/LROnapi_gro_receive()재조립 후 메타데이터 일관성
VLAN offloadNETIF_F_HW_VLAN_CTAG_TX/RXtag insert/strip와 통계 동기화

PTP 하드웨어 타임스탬프와 시간 동기화

금융/통신/분산 DB 워크로드에서는 네트워크 성능만큼 시간 정확도가 중요합니다. PTP 지원 NIC 드라이버는 SIOCSHWTSTAMP 설정, TX timestamp completion, PHC 노출을 안정적으로 제공해야 합니다.

/* 개념 예시: HW timestamp 사용자 요청 검증 */
static int my_hwtstamp_set(struct net_device *ndev, struct ifreq *ifr)
{
    struct hwtstamp_config cfg;

    if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
        return -EFAULT;

    if (cfg.tx_type != HWTSTAMP_TX_OFF && cfg.tx_type != HWTSTAMP_TX_ON)
        return -ERANGE;

    my_hw_config_timestamp(ndev, &cfg);

    if (copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)))
        return -EFAULT;

    return 0;
}

SR-IOV, representor, 스위치 모드 전환

클라우드 환경에서는 PF/VF 분리와 representor netdev 운영이 기본입니다. 드라이버는 legacy 모드와 switchdev 모드 전환 시 control-plane 일관성을 보장해야 합니다.

/* 개념 예시: eswitch 모드 전환과 대표자 netdev 동기화 */
static int my_eswitch_mode_set(struct my_priv *priv, u16 mode)
{
    if (mode == DEVLINK_ESWITCH_MODE_SWITCHDEV)
        return my_enable_representors(priv);
    if (mode == DEVLINK_ESWITCH_MODE_LEGACY)
        return my_disable_representors(priv);

    return -EOPNOTSUPP;
}
운영 포인트: VF reset, 링크 이벤트, tc offload rule 삭제 시 PF/VF/representor 간 상태 동기화가 어긋나면 패킷 블랙홀이나 정책 누락이 발생합니다.

검증 매트릭스: 릴리스 전 체크 항목

영역필수 검증합격 기준 예시
기능up/down, MTU, VLAN, bridge, bond10k회 반복 시 누수/lockup 없음
성능단일/다중 스트림, 작은 패킷/점보 프레임목표 PPS/Gbps 달성, drop rate 임계 이내
안정성link flap, reset storm, hotplug, suspend/resume자동 복구 성공, 수동 재로드 불필요
가시성ethtool/stat/devlink health dump장애 원인 식별 가능한 텔레메트리 제공
보안XDP/tc rule 경계값, malformed packetcrash 없이 drop/에러 처리

TX 큐 선택: ndo_select_queue, XPS, CPU locality

멀티큐 NIC에서 ndo_start_xmit() 성능은 큐 선택 품질에 크게 좌우됩니다. 플로우 해시, CPU affinity, XPS 정책이 맞지 않으면 lock 경합과 cache miss가 급증합니다.

/* 개념 예시: qdisc/XPS 힌트를 반영한 TX queue 선택 */
static u16 my_ndo_select_queue(struct net_device *dev, struct sk_buff *skb,
                               struct net_device *sb_dev)
{
    u32 hash = skb_get_hash(skb);
    u16 q = reciprocal_scale(hash, dev->real_num_tx_queues);

    /* 로컬 CPU 우선 정책이 있으면 q를 재매핑 */
    q = my_xps_remap(dev, q, raw_smp_processor_id());
    return q;
}

Doorbell 메커니즘과 DMA 메모리 배리어

약한 메모리 모델 CPU(ARM64 등)에서 descriptor write와 doorbell MMIO write의 순서가 보장되지 않으면 간헐적 TX hang이 생깁니다. 게시 경로에서 barrier 사용 규칙을 문서화해야 합니다.

Doorbell이란?

Doorbell은 PCIe BAR 공간의 장치 레지스터에 MMIO write를 수행하여 NIC에 새 작업(디스크립터)이 준비되었음을 알리는 메커니즘입니다. Doorbell이 없으면 NIC이 링 버퍼를 지속적으로 폴링해야 하며, 이는 PCIe 대역폭 낭비와 전력 소모 증가를 초래합니다.

Doorbell의 핵심 속성은 다음과 같습니다.

서브시스템별 Doorbell 비교

Doorbell 메커니즘은 NIC뿐 아니라 다양한 PCIe 디바이스 서브시스템에서 사용됩니다. 아래 표는 주요 서브시스템별 doorbell 특성을 비교합니다.

서브시스템Doorbell 대상기록 값최적화 기법
NIC TXTail Pointer 레지스터마지막 디스크립터 인덱스배치 게시
NVMeSQ Tail Doorbell큐 tail 포인터Shadow Doorbell Buffer
xHCI (USB 3.x)Doorbell ArrayEP 인덱스스트림 기반 배치
NTB (PCI)Doorbell 비트맵이벤트 비트비트마스크
CPU: desc 작성 TX Ring (DMA 메모리) dma_wmb() writel() Doorbell (MMIO write) NIC HW (DMA fetch) dma_wmb()는 descriptor 쓰기가 doorbell MMIO 쓰기보다 먼저 디바이스에 보이도록 보장합니다.

Doorbell 최적화 기법

Doorbell은 PCIe MMIO 트랜잭션이므로 호출 빈도를 줄이는 것이 성능 최적화의 핵심입니다. 주요 기법은 다음과 같습니다.

/* 개념 예시: doorbell 전 메모리 배리어 보장 */
static void my_post_tx_desc(struct my_priv *priv, struct my_desc *d)
{
    priv->tx_ring[d->idx] = *d;

    /* descriptor 메모리 write 완료 보장 */
    dma_wmb();

    /* 이후 doorbell write */
    writel(d->idx, priv->tx_doorbell);
}
상황권장 배리어목적
descriptor → MMIO doorbelldma_wmb()디바이스가 완전한 descriptor만 보도록 보장
MMIO status read 후 메모리 참조dma_rmb()완료 상태와 data buffer ordering 보장
일반 CPU 공유 데이터smp_wmb/rmb소프트웨어 스레드 간 ordering 보장

Busy Poll/NAPI 조합과 지연 최적화

초저지연 워크로드에서는 interrupt moderation보다 busy-poll이 유리할 수 있습니다. 드라이버는 NAPI 상태 전이를 안정적으로 유지해 busy-poll 사용자와 일반 트래픽이 충돌하지 않게 해야 합니다.

# 실습 예제: busy poll 파라미터 조정 및 즉시 확인
# 소켓 단위 busy poll (마이크로초)
sysctl -w net.core.busy_poll=50
sysctl -w net.core.busy_read=50

# NIC interrupt moderation과 함께 튜닝
ethtool -C eth0 rx-usecs 0
ethtool -C eth0 tx-usecs 0
주의: busy-poll은 tail latency를 낮출 수 있지만 CPU 사용률을 크게 증가시킵니다. 배치 처리 워크로드와 혼재 시에는 cpuset/isolcpus로 busy-poll 전용 CPU를 분리하는 편이 안전합니다.

회귀 테스트: packetdrill, kselftest, fault injection

netdev 드라이버는 환경 의존성이 커서 재현 테스트가 어렵습니다. 릴리스 전에 최소 회귀 시나리오를 자동화하면 “간헐적 링크 다운” 같은 문제를 조기에 차단할 수 있습니다.

도구테스트 대상예시
kselftest (net)기능 회귀MTU/VLAN/GRO/GSO 기본 동작
packetdrill프로토콜 타이밍/에러 경로재전송, out-of-order, checksum 에러
tc + iperf3성능/큐 안정성장시간 부하 중 drop/timeout 감시
fault injection복구 경로DMA map 실패, TX timeout, reset 반복

QoS/DCB: mqprio, ETS, PFC 운영 포인트

데이터센터 환경에서는 대역폭 분배와 무손실 트래픽 제어가 중요합니다. 드라이버가 mqprio, DCB, PFC를 부분 지원하는 경우 지원 범위를 명확히 노출해야 운영 오해를 줄일 수 있습니다.

# 실습 예제: mqprio/ethtool로 큐 정책 검증
# mqprio qdisc 예시 (TC별 큐 매핑)
tc qdisc replace dev eth0 root mqprio num_tc 4 \
  map 0 1 2 3 3 3 3 3 \
  queues 1@0 1@1 2@2 4@4 hw 1

# DCB/PFC 상태 확인 예시 (환경별 도구 상이)
dcbtool gc eth0 dcb
ethtool --show-priv-flags eth0
항목드라이버 책임실패 시 증상
TC→queue 매핑qdisc 설정과 HW scheduler 동기화특정 클래스 starvation
PFCpriority별 pause on/off 적용drop 급증 또는 head-of-line blocking
ETSbandwidth share를 HW arbitration에 반영대역폭 분배 불일치

PREEMPT_RT와 NAPI threaded 모드

실시간 커널에서는 IRQ/softirq 모델이 일반 커널과 다르게 동작합니다. 드라이버는 spinlock 길이를 줄이고, napi poll 지연 상한을 보장하도록 설계해야 합니다.

Netpoll/kdump 경로 지원

패닉 상황에서 네트워크 로그 덤프가 필요하면 netpoll/netconsole 경로가 사용됩니다. 일반 데이터 경로와 독립된 최소 송신 경로를 유지해야 crash dump 신뢰성이 올라갑니다.

/* 개념 예시: netpoll 경로의 재진입/잠금 제약 */
static void my_netpoll_send_skb(struct net_device *ndev, struct sk_buff *skb)
{
    struct my_priv *priv = netdev_priv(ndev);

    /* 최소 TX 경로: sleep 금지, 동적 메모리 할당 최소화 */
    if (!my_tx_ring_has_space(priv)) {
        dev_kfree_skb_any(skb);
        return;
    }
    my_map_skb_to_tx_desc(priv, skb);
    my_ring_doorbell(priv);
}
운영 주의: kdump 캡처 커널에서 동일 NIC 드라이버가 정상 동작하는지 별도 검증이 필요합니다. 본 커널에서 정상이어도 캡처 커널 initramfs/펌웨어 누락으로 전송 실패가 발생할 수 있습니다.

보안 하드닝: 입력 검증과 경계 조건

네트워크 드라이버 취약점은 원격 트리거 가능성이 있습니다. 길이 검증, ring index 범위 확인, DMA 주소 검증은 성능 최적화보다 우선되어야 합니다.

취약 패턴점검 포인트방어 전략
RX length 신뢰HW가 넘긴 length를 그대로 사용최소/최대 길이, headroom 검증
ring index overflowproducer/consumer wrap 처리 누락mask 기반 인덱싱 + assert
UAF on resetreset 중 skb/page 소유권 경합state machine + refcount 엄격화
ioctl/netlink 입력 검증 부족사용자 파라미터 경계값 누락range check, capability check

가상 netdev: TUN/TAP, veth, virtio-net

가상 인터페이스도 본질적으로는 net_device입니다. 차이는 “패킷을 어디로 내보내는가”에 있습니다. 물리 NIC는 DMA 링으로, TUN/TAP은 파일 디스크립터로, veth는 peer netdev로 전달합니다.

Kernel Network Stack ip_rcv / dev_queue_xmit 공통 sk_buff 경로 Physical NIC Driver RX/TX Ring + DMA e1000e/ixgbe/mlx5 TUN/TAP Driver /dev/net/tun read/write 유저스페이스 VPN/QEMU veth / virtio-net peer queue / virtqueue 패킷 종착지 하드웨어 링크 유저스페이스 프로세스 네임스페이스 peer 가상머신 virtio backend 모두 net_device 추상화 재사용
물리/가상 인터페이스는 전송 매체만 다를 뿐 동일한 netdev 운영 모델을 공유합니다.
/* 개념 예시: drivers/net/tun.c 핵심 경로 요약 */
static int tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
{
    struct tun_struct *tun = netdev_priv(dev);

    if (!ptr_ring_produce(&tun->tx_ring, skb))
        return NETDEV_TX_OK;

    /* 유저스페이스가 fd read()로 수신 */
    dev_kfree_skb_any(skb);
    return NETDEV_TX_OK;
}

동기화, RTNL, 메모리 모델

netdev 코드의 동기화는 단일 락으로 끝나지 않습니다. 설정 경로는 RTNL, 데이터 경로는 per-queue spinlock/NAPI, 통계 경로는 u64_stats_sync를 조합합니다.

RTNL per-namespace 락 (v6.13+): 커널 6.13에서 rtnl_lockper-network namespace 단위로 세분화되었습니다. 기존에는 전체 네트워크 네임스페이스가 단일 글로벌 RTNL 뮤텍스를 공유하여, 컨테이너 수천 개를 운영하는 환경에서 제어 경로 병목이 발생했습니다. per-namespace RTNL 락을 통해 서로 다른 네임스페이스의 링크 설정 작업이 병렬로 수행되어 경합이 크게 감소합니다.

디버깅 체크리스트와 실전 트러블슈팅

증상의심 지점확인 방법
TX 멈춤queue wake 누락, completion path 손상ethtool -S, netif_tx_queue_stopped() 추적
RX drop 급증NAPI budget 과소, ring refill 지연/proc/net/softnet_stat, RX no-buffer 카운터
링크 flapPHY state machine/interrupt stormdmesg, phylink tracepoint
고부하에서 패킷 손실IRQ affinity/NUMA 불일치/proc/interrupts, ethtool -x/-X
XDP 적용 후 비정상page_pool recycle/XDP verdict 처리 오류bpftool prog, drop reason trace
# 실습 예제: 큐/오프로드/통계 빠른 점검
# 큐/오프로드/통계 빠른 점검
ethtool -i eth0
ethtool -k eth0
ethtool -l eth0
ethtool -S eth0 | grep -E "drop|error|timeout|busy"

# 소프트넷 병목 확인
cat /proc/net/softnet_stat

# netdev 관련 tracepoint 예시
trace-cmd record -e net -e napi -e skb

운영 관측: 필수 대시보드 지표

드라이버 품질은 장애가 났을 때 “원인을 빠르게 찾을 수 있는가”로 평가됩니다. 아래 지표를 대시보드로 상시 수집하면 회귀를 조기에 감지할 수 있습니다.

지표 그룹필수 항목경보 조건 예시
링크 상태link up/down flap 횟수, 속도/duplex 변경10분 내 flap 3회 이상
RX/TX 에러crc/frame/rx_missed/tx_timeout분당 에러 증가율 급등
큐 불균형queue별 packet/byte 편차, backlog상위 큐 편중 70% 초과
복구 이벤트reset 횟수, devlink health recover 횟수하루 1회 이상 자동 리셋
지연 품질p99/p999 RTT, drop reason 통계tail latency 임계 초과

Bring-up 30분 체크리스트

  1. 장치 인식 확인
    lspci -nn, dmesg | grep -i <driver>로 probe 성공 여부 확인
  2. 링크 기본 동작
    ip link set dev eth0 up, ethtool eth0로 speed/duplex/link 검증
  3. 기본 송수신
    ping, 단일 iperf3로 RX/TX 모두 정상 동작 확인
  4. 오프로드/큐 설정
    ethtool -k/-l/-x 확인 후 장비 정책에 맞게 조정
  5. 에러 카운터 스냅샷
    ethtool -S eth0 초기값 저장, 10분 부하 후 증분 비교
  6. 리셋 복구
    의도적 link flap 또는 함수 리셋 후 자동 복구 성공 여부 확인
  7. 관측/알람 연계
    링크/에러/reset 지표가 모니터링 시스템에 수집되는지 검증

코드 리뷰 체크리스트 (net_device 전용)

제조사별 NIC 드라이버 상세 매트릭스

같은 net_device 모델이라도 벤더별로 펌웨어 의존성, 오프로드 범위, reset 전략이 다릅니다. 운영 환경에서는 “벤더별 특성”을 분리해서 튜닝/장애 대응해야 재현성이 올라갑니다.

Intel: e1000e / igb / ixgbe / i40e / ice / idpf

Intel은 리눅스 네트워크 드라이버 생태계에서 가장 오랜 역사와 가장 넓은 드라이버 포트폴리오를 보유하고 있습니다. 1GbE 데스크톱용 e1000e부터 100GbE 데이터센터용 ice, 그리고 차세대 가상화 인터페이스 idpf까지 세대별 설계 철학이 뚜렷하게 달라집니다. 각 드라이버의 아키텍처 차이를 이해하면 성능 튜닝과 장애 진단 전략을 드라이버 세대에 맞춰 정확히 수립할 수 있습니다.

드라이버주요 세대/용도핵심 포인트Max QueuesXDP 지원자주 보는 이슈
e1000e1GbE 서버/임베디드안정성 우선, 기능 단순1 (싱글 큐)미지원링크 flap, 절전 전환 후 wake 지연
igb1GbE 멀티큐RSS/TSO 기본, SR-IOV 일부 모델8지원 (5.9+)큐 불균형, IRQ affinity 미스매치
ixgbe10GbE(82599/X540)Flow Director, DCB, XDP 일부 경로128지원 (4.17+)tx timeout, FDIR rule 관리 복잡도
i40eXL710/X710(40/10GbE)VF 관리, DDP/firmware 의존성64 (per PF)지원 (5.1+)firmware 호환성, reset 후 VF 상태 불일치
iceE810(100GbE)devlink/representor, 고급 tc offload256지원 (5.5+)DDP 패키지 불일치, eswitch 설정 충돌
idpf신규 인프라 VF/가상화 경로queue model/virtchnl 중심 설계가변 (CP 결정)계획중PF-VF 제어채널 상태 불일치
# 실습 예제: Intel 계열 NIC 운영 점검 루틴
# Intel 계열 공통 점검
ethtool -i eth0
ethtool -S eth0 | grep -Ei "fdir|tx_timeout|rx_missed|reset"
dmesg | grep -Ei "ixgbe|i40e|ice|idpf|firmware|ddp"

# 드라이버별 모듈 파라미터 확인
modinfo e1000e | grep parm
modinfo ice | grep parm

e1000e: 싱글 큐 레거시 아키텍처

e1000e는 Intel PRO/1000 계열 PCIe GbE 컨트롤러를 위한 드라이버로, 싱글 TX/RX 큐 모델을 채택합니다. 멀티큐를 지원하지 않기 때문에 RSS, 큐별 NAPI 분리, XDP 등 최신 데이터 경로 최적화를 적용할 수 없지만, 단순한 구조 덕분에 안정성이 매우 높아 서버 관리 인터페이스(BMC/IPMI 전용 포트)와 임베디드 환경에서 여전히 널리 사용됩니다.

e1000e 싱글 큐 데이터 경로 ── TX 경로 ── Application Socket TCP/IP Stack Single TX Queue (ndo_start_xmit) DMA TX NIC PHY → Wire ── RX 경로 ── NIC PHY Wire → DMA RX Single RX Queue (NAPI poll) TCP/IP Stack Socket Application 단일 IRQ → 단일 NAPI 인스턴스 CPU 하나에 모든 처리 집중 특징: • 싱글 큐 → 멀티코어 CPU에서 병렬 처리 불가 • MSI 인터럽트 1개 → IRQ affinity 분산 불가 • XDP/AF_XDP 미지원 • 최대 1Gbps, PCIe 1x 슬롯에도 대역폭 충분
e1000e 싱글 큐 데이터 경로: TX/RX 모두 단일 큐와 단일 NAPI 인스턴스로 처리

e1000e가 지원하는 주요 칩 변형은 다음과 같습니다. 세대별로 전력 관리와 부가 기능에 차이가 있으며, 특히 I219는 Intel vPro 플랫폼의 AMT(Active Management Technology) 트래픽을 처리하는 ME(Management Engine) 연동 경로가 있어 드라이버 동작이 미묘하게 다릅니다.

칩 변형대표 제품최대 속도PCIe Gen전력 상태WoLHW Timestamp비고
82574L독립형 PCIe GbE1GbpsGen1 x1D0/D3hot지원미지원가장 안정적인 레거시 칩
I217-V/LMHaswell LPC 통합1Gbps내부 버스D0/D3hot/D3cold지원미지원PHY 내장, Low Power Idle(EEE)
I219-V/LMSkylake+ PCH 통합1Gbps내부 버스D0/D3hot/D3cold지원미지원ME 연동, vPro AMT 트래픽 분리
알려진 링크 flap 이슈: I217/I219 칩셋에서 EEE(Energy Efficient Ethernet)가 활성화된 상태로 특정 스위치와 연결하면 주기적인 링크 다운/업 반복(link flap)이 발생할 수 있습니다. 운영 환경에서는 ethtool --set-eee eth0 eee off로 EEE를 비활성화하고, D3cold 진입 후 WoL 복구가 느린 경우 BIOS에서 Deep Sleep 옵션을 비활성화하세요.
전력 관리 최적화: e1000e는 S0ix(Modern Standby) 환경에서 D3cold 상태로 진입하여 전력 소비를 최소화할 수 있습니다. 다만 D3cold에서 복구 시 PHY 재초기화에 최대 2~3초가 소요되므로, 저지연 WoL이 필요한 환경에서는 D3hot까지만 허용하는 것이 안전합니다.
# 실습 예제: e1000e 진단 및 전력 관리
# 드라이버/칩 정보 확인
ethtool -i eth0
# 출력 예: driver: e1000e, firmware-version: 0.13-4, bus-info: 0000:00:1f.6

# EEE 상태 확인 및 비활성화 (링크 flap 방지)
ethtool --show-eee eth0
ethtool --set-eee eth0 eee off

# WoL 설정 확인 및 활성화
ethtool -s eth0 wol g
ethtool -s eth0 wol d   # WoL 비활성화

# 셀프 테스트 (offline 모드: 링크 일시 단절)
ethtool -t eth0 offline

# 링크 flap 이력 확인
dmesg | grep -i "e1000e.*link\|e1000e.*changed"
ethtool -S eth0 | grep -E "rx_no_buffer|tx_timeout|rx_crc_errors"

igb: 멀티큐 RSS 모델

igb는 Intel I350, I210, I211 계열 GbE 컨트롤러를 지원하며, e1000e와 달리 멀티큐 RSS(Receive Side Scaling)를 기본 지원합니다. 서버급 환경에서 1GbE 인터페이스를 고밀도로 운영하거나, PTP(Precision Time Protocol) 하드웨어 타임스탬프가 필요한 경우 igb가 권장됩니다.

igb RSS 멀티큐 RX 흐름 Incoming Packet L3/L4 Parser IP/TCP 헤더 추출 Toeplitz Hash RSS Key (40B) → Indirection Table RX Queue 0 RX Queue 1 RX Queue 2 RX Queue 3 NAPI poll[0] NAPI poll[1] NAPI poll[2] NAPI poll[3] Network Stack (GRO) MSI-X 인터럽트 큐당 독립 IRQ벡터 CPU별 affinity 설정 igb RSS 핵심: • Toeplitz 해시로 동일 플로우를 항상 같은 큐에 배치 (flow affinity 보장) • I350: 최대 8큐, I210: 최대 4큐, I211: 최대 2큐 • ethtool -X로 indirection table 커스터마이징 가능 • XDP 지원 (커널 5.9+), AF_XDP ZC 지원 (커널 5.14+)
igb RSS 멀티큐 흐름: 패킷 → L3/L4 파싱 → Toeplitz 해시 → 큐 분배 → per-queue NAPI → Network Stack
칩 변형포트 수최대 큐SR-IOV VFPTP HW TSPCIe Gen비고
I3502/48최대 8 VF소프트웨어 TSGen2 x4서버용 멀티포트
I21014미지원하드웨어 TSGen2 x1PTP 최적, Qbv TSN 지원
I21112미지원미지원Gen1 x1데스크톱/NAS용 저가
PTP 하드웨어 타임스탬프: I210은 IEEE 1588 PTP v2 하드웨어 타임스탬프를 직접 지원하는 유일한 igb 칩입니다. 나노초 수준의 시간 동기화가 필요한 금융 거래 시스템이나 산업용 TSN(Time-Sensitive Networking) 환경에서는 I210을 선택하세요. I350은 소프트웨어 타임스탬프만 지원하므로 마이크로초 수준 정밀도에 한계가 있습니다.
# 실습 예제: igb RSS 및 PTP 설정
# RSS 큐 수 확인 및 변경
ethtool -l eth0
ethtool -L eth0 combined 4

# RSS indirection table 확인
ethtool -x eth0
# indirection table을 큐 0,1에만 집중
ethtool -X eth0 equal 2

# RSS 해시 키 확인
ethtool -x eth0 | head -5

# SR-IOV VF 생성 (I350 전용)
echo 4 > /sys/class/net/eth0/device/sriov_numvfs
ip link show eth0

# PTP 하드웨어 타임스탬프 설정 (I210)
ethtool -T eth0
# ptp4l로 PTP 동기화 실행
ptp4l -i eth0 -m -H
# phc2sys로 시스템 클록 동기화
phc2sys -s eth0 -c CLOCK_REALTIME -O 0 -m

# IRQ affinity 확인
grep eth0 /proc/interrupts
cat /proc/irq/*/smp_affinity_list | head -8

ixgbe: 10GbE 고성능 아키텍처

ixgbe는 Intel 82599, X540, X550 시리즈 10GbE 컨트롤러를 지원하며, Linux 10GbE 시장에서 가장 오래 검증된 드라이버입니다. 128개 TX/RX 큐, Flow Director, DCB(Data Center Bridging), SR-IOV(최대 64 VF) 등 데이터센터급 기능을 폭넓게 제공합니다.

ixgbe 내부 아키텍처 (82599/X540/X550) PCIe Interface Gen2 x8 (82599) Gen3 x4 (X540) Gen3 x4 (X550) DMA Engine TX DMA: 128 desc rings RX DMA: 128 desc rings Head/Tail 레지스터 TX Queues 최대 128개 (TC별 분배) TSO/Checksum Offload RX Queues 최대 128개 (RSS/FDIR) LRO/RSC 지원 MAC 10GbE/1GbE 선택 Flow Control/PFC PHY/SFP+ SFP+ / Base-T DAC 케이블 Wire Flow Director ATR (자동) / Perfect Filter 8K 필터 엔트리 DCB / PFC 8 Traffic Class ETS 대역폭 배분 SR-IOV 최대 64 VF VF MAC/VLAN 관리 세대별 차이: • 82599: PCIe Gen2 x8, SFP+ 전용, Flow Director ATR/Perfect • X540: PCIe Gen3 x4, Base-T(RJ45) 통합, MACsec 미지원 • X550: PCIe Gen3 x4, Base-T + SFP+, VXLAN/GENEVE 오프로드 • 공통: XDP 지원 (4.17+), RSS 128큐, SR-IOV 64 VF • X550-T2: 10GBASE-T 듀얼포트, NBASE-T(2.5G/5G) 네고시에이션 지원
ixgbe 내부 아키텍처: PCIe → DMA → TX/RX 큐(128) → MAC → PHY, Flow Director와 DCB는 큐 선택 및 스케줄링에 관여
ixgbe Flow Director: ATR vs Perfect Filter RX Packet Wire에서 수신 FDIR 매치? No Match ATR (자동 학습) TX 시 5-tuple 해시 기록 → RX 시 같은 CPU 큐 배치 Match Perfect Filter ethtool -N 규칙 매칭 → 지정 큐로 직접 스티어링 Target RX Queue NAPI poll → Stack CPU locality 보장 FDIR 비활성 RSS Hash 분배 기본 큐 선택 방식 ATR vs Perfect Filter 비교: • ATR: 자동 학습, 설정 불필요, TX→RX CPU locality 최적화, 대부분의 워크로드에 적합 • Perfect: 수동 규칙(ethtool -N), 정확한 큐 지정, 지연시간 민감 트래픽에 사용, 최대 8K 엔트리 • ATR과 Perfect Filter는 동시 사용 가능 — Perfect 규칙이 ATR보다 우선순위가 높음
ixgbe Flow Director: ATR(자동 학습)과 Perfect Filter(수동 규칙)의 패킷 스티어링 경로 비교
기능82599 (X520)X540X550
최대 속도10Gbps (SFP+)10Gbps (Base-T)10Gbps (Base-T/SFP+)
PCIeGen2 x8Gen3 x4Gen3 x4
RSS 큐128128128
SR-IOV VF646464
Flow DirectorATR + Perfect (8K)ATR + Perfect (8K)ATR + Perfect (8K)
DCB (TC)8 TC8 TC8 TC
XDP지원 (4.17+)지원 (4.17+)지원 (4.17+)
VXLAN/GENEVE 오프로드미지원미지원지원
NBASE-T (2.5G/5G)미지원미지원지원 (X550-T)
MACsec미지원미지원일부 SKU
DCB/PFC 구성 팁: ixgbe에서 DCB를 활성화하면 RSS 큐 수가 Traffic Class 수에 의해 제한됩니다. 예를 들어 8 TC를 활성화하면 큐는 TC당 최대 16개(128/8)로 분배됩니다. DCB와 SR-IOV를 동시에 사용하는 경우 VF당 할당 가능한 TC는 PF 설정에 종속됩니다.
tx_timeout 패턴: 82599에서 높은 PPS 환경에서 tx_timeout이 간헐적으로 발생하는 경우, tx_ring->next_to_usenext_to_clean 간 격차가 TX descriptor ring 크기에 근접했는지 확인하세요. ethtool -G eth0 tx 4096으로 ring 크기를 늘리면 완화될 수 있습니다.
# 실습 예제: ixgbe 성능 튜닝 및 Flow Director
# 큐/링 설정 확인
ethtool -l eth0          # 큐 수 확인
ethtool -g eth0          # 링 크기 확인
ethtool -G eth0 rx 4096 tx 4096  # 링 크기 증가

# Flow Director ATR 활성화 확인
ethtool -k eth0 | grep ntuple
ethtool -K eth0 ntuple on

# Perfect Filter 규칙 추가
ethtool -N eth0 flow-type tcp4 src-ip 10.0.0.1 dst-port 443 action 0
ethtool -N eth0 flow-type udp4 dst-port 4789 action 4  # VXLAN

# 활성 FDIR 규칙 및 통계
ethtool -n eth0
ethtool -S eth0 | grep fdir

# DCB/PFC 설정 (lldptool 사용)
lldptool -T -i eth0 -V PFC enableTx=yes
lldptool -T -i eth0 -V ETS-CFG willing=no \
  up2tc=0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7 \
  tsa=0:ets,1:ets,2:ets,3:ets,4:ets,5:ets,6:ets,7:ets \
  tcbw=12,12,12,12,13,13,13,13

# XDP 프로그램 로딩
ip link set dev eth0 xdpdrv obj xdp_prog.o sec xdp

# IRQ affinity 최적화 (CPU당 큐 1:1 매핑)
# Intel 제공 set_irq_affinity 스크립트 사용
./set_irq_affinity.sh local eth0

# 성능 모니터링
ethtool -S eth0 | grep -E "rx_bytes|tx_bytes|rx_packets|tx_packets"
ethtool -S eth0 | grep -E "tx_timeout|rx_missed|fdir"

i40e: AdminQ 기반 제어 경로

i40e는 Intel XL710/X710/XXV710 시리즈(10/25/40GbE)를 지원하며, 이전 세대(ixgbe)와 근본적으로 다른 AdminQ(Admin Queue) 기반 펌웨어 제어 모델을 도입했습니다. 드라이버가 하드웨어 레지스터를 직접 조작하는 대신, AdminQ 메시지를 통해 펌웨어에 명령을 전달하는 구조입니다. 이 설계는 SR-IOV VF 관리에서 virtchnl 프로토콜로 확장되어, PF 드라이버가 VF의 리소스 요청을 중재합니다.

i40e PF-VF 아키텍처: AdminQ + virtchnl PF 드라이버 (i40e) 데이터 경로 관리 VF 리소스 중재 ethtool/devlink 인터페이스 AdminQ 비동기 명령 큐 ATQ(요청) + ARQ(응답) FW 이벤트 알림 Firmware (FW) 스위치 로직 / 필터 관리 DDP 파서 파이프라인 PHY/링크 제어 VF0 (iavf) Guest VM #1 VF1 (iavf) Guest VM #2 VFn (iavf) Guest VM #n virtchnl (VF ↔ PF 메시지 프로토콜) 공유 리소스 • VSI(Virtual Station Interface) 풀 • 큐 페어 (최대 1536 per device) • 필터 엔트리, 인터럽트 벡터 핵심 구조: • PF가 AdminQ를 통해 FW와 통신, VF는 virtchnl을 통해 PF에 리소스 요청 • VF 드라이버(iavf)는 하드웨어를 직접 접근하지 않고, PF를 통해 간접 제어 • 최대 128 VF, 1536 큐 페어 풀
i40e PF-VF 아키텍처: PF는 AdminQ로 펌웨어와 통신하고, VF는 virtchnl로 PF에 리소스를 요청
AdminQ 명령 카테고리대표 명령설명
스위치/필터ADD_FILTER, REMOVE_FILTERMAC/VLAN/클라우드 필터 추가/삭제
VSI 관리ADD_VSI, UPDATE_VSI가상 스테이션 인터페이스 생성/수정
큐 설정CONFIG_VSI_QUEUESTX/RX 큐 매핑 및 파라미터 설정
링크 관리GET_LINK_STATUS, SET_PHY_CONFIG링크 상태 조회, PHY 설정 변경
RSSSET_RSS_KEY, SET_RSS_LUTRSS 해시 키 및 indirection table 설정
NVM/FWNVM_UPDATE, GET_FW_VERSION펌웨어 업데이트, 버전 조회
VF 제어SET_VF_CONFIG, RESET_VFVF 리소스 할당, VF 리셋
DDP(Dynamic Device Personalization): i40e도 DDP를 지원하지만 ice와 달리 DDP 없이도 대부분의 기능이 정상 동작합니다. i40e DDP는 GTP, PPPoE 등 비표준 프로토콜 파싱을 위한 선택적 확장 기능으로, 텔레코어/5G 환경에서 주로 사용됩니다.
FW 버전 범위지원 커널주요 기능 변경알려진 이슈
6.x4.x ~ 5.4기본 AdminQ, VF 128개VF reset 후 큐 불일치
7.x5.4 ~ 5.15Cloud Filter, 향상된 VF trust일부 FW/드라이버 조합에서 link flap
8.x ~ 9.x5.15+DDP 지원, ADQ(Application Device Queue)ADQ + SR-IOV 동시 사용 시 성능 저하
비교 항목i40e (XL710)ice (E810)
최대 속도40GbE100GbE
FW 제어 모델AdminQAdminQ (확장)
VF 프로토콜virtchnl v1virtchnl v1 + v2 (idpf)
DDP 필수 여부선택적필수 (없으면 Safe Mode)
eswitch 모드미지원지원 (switchdev)
devlink 통합기본적풍부 (health, flash, param)
tc offload제한적flower/u32 offload
PTP 정밀도소프트웨어 TS하드웨어 TS (ns)
XDP지원 (5.1+)지원 (5.5+)
# 실습 예제: i40e AdminQ 및 VF 관리
# 펌웨어 버전 확인
ethtool -i eth0
devlink dev info pci/0000:03:00.0

# AdminQ 이벤트 로그 확인
dmesg | grep -i "i40e.*adminq\|i40e.*aq\|i40e.*fw"

# VF 생성 및 관리
echo 8 > /sys/class/net/eth0/device/sriov_numvfs
ip link set eth0 vf 0 mac 00:11:22:33:44:55
ip link set eth0 vf 0 vlan 100
ip link set eth0 vf 0 trust on
ip link set eth0 vf 0 rate 1000   # TX rate 제한 (Mbps)

# ADQ (Application Device Queue) 설정
# TC 4개 생성, 각각 큐 4개 할당
tc qdisc add dev eth0 root mqprio num_tc 4 \
  map 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3 \
  queues 4@0 4@4 4@8 4@12 hw 1 mode channel

# VF 통계 모니터링
ethtool -S eth0 | grep "vf_"
ip -s link show eth0

idpf: virtchnl2 기반 차세대 인프라 드라이버

idpf(Infrastructure Data Path Function)는 Intel의 차세대 네트워크 드라이버로, 하드웨어 추상화를 극대화한 virtchnl2 프로토콜 기반 설계입니다. 기존 iavf 드라이버가 특정 세대의 PF(i40e/ice)에 종속적이었던 것과 달리, idpf는 Control Plane(CP)과 Data Plane을 완전히 분리하여 모든 세대의 Intel NIC에 대한 통합 VF/인프라 드라이버 역할을 목표로 합니다.

idpf virtchnl2 제어 경로 idpf 드라이버 • 하드웨어 비종속적 설계 • Split/Single Queue 선택 • netdev 등록/해제 • 런타임 큐 재설정 virtchnl2 Mailbox Queue • 제어 메시지 송수신 • Capability 협상 • 큐 그룹 설정 • 이벤트 알림 Control Plane (CP) • 리소스 할당/해제 • 큐 모델 결정 (Split/Single) • 필터/RSS 규칙 관리 • MAC/VLAN 정책 적용 HW/FW/SW CP 구현 가능 (FPGA, IMC FW, 소프트웨어 등) Data Path (TX/RX) DMA 직접 접근 (성능 경로) HW Queue Pairs TX/RX Descriptor Rings DMA (데이터 경로 — CP 개입 없음) 핵심 설계 원칙: • 제어 경로(virtchnl2 + Mailbox)와 데이터 경로(DMA)가 완전히 분리 • CP는 HW FW, FPGA, 또는 소프트웨어로 구현 가능 → 드라이버 코드 변경 없이 다양한 플랫폼 지원
idpf virtchnl2 제어 경로: 드라이버 → Mailbox Queue → Control Plane, 데이터 경로는 DMA 직접 접근
idpf 큐 모델: Split Queue vs Single Queue Split Queue 모델 (기본) TX 측 TX Queue (전송 요청) TX Completion Queue RX 측 RX Buffer Queue RX Completion Queue Split Queue 장점 • 버퍼 보충과 완료 처리가 독립적으로 수행 • Completion Queue를 여러 Buffer Queue가 공유 가능 • HW가 out-of-order 완료 처리 최적화 가능 • 캐시 효율성 향상 (Hot/Cold 데이터 분리) • in-order 보장 불필요 → 파이프라인 병렬화 • 대규모 VF 환경에서 큐 리소스 효율적 사용 Single Queue 모델 (호환) TX 측 TX Queue (전송 + 완료 통합) RX 측 RX Queue (수신 + 버퍼 보충 통합) Single Queue 용도 • 기존 iavf/i40e/ice와 호환 필요 시 • 구조가 단순하여 디버깅 용이 • 소규모 환경에서 오버헤드 최소화 • 레거시 게스트 OS 지원 • CP 협상 시 Split 미지원 fallback
idpf 큐 모델 비교: Split Queue(기본)는 전송/완료를 분리하여 파이프라인 병렬화, Single Queue(호환)는 기존 모델과 동일
iavf 대체 전략: idpf는 장기적으로 iavf(Intel Adaptive Virtual Function) 드라이버를 대체할 예정입니다. iavf는 virtchnl v1에 종속되어 특정 PF 드라이버(i40e/ice)와만 호환되지만, idpf의 virtchnl2는 프로토콜 수준에서 capability 협상을 수행하므로 PF 세대에 독립적입니다. 커널 6.4부터 idpf가 메인라인에 포함되었으며, 새 배포에서는 idpf 사용을 권장합니다.
비교 항목iavf (virtchnl v1)idpf (virtchnl v2)
PF 종속성i40e/ice PF 필수CP 구현 독립적
큐 모델Single Queue 고정Split/Single 선택 가능
Capability 협상고정된 기능 세트런타임 동적 협상
큐 재설정전체 리셋 필요개별 큐 그룹 재설정
메인라인 커널4.2+6.4+
유지보수 상태유지보수 모드적극 개발중
RDMA 지원미지원계획중
XDP제한적계획중
# 실습 예제: idpf 드라이버 확인 및 큐 모델 점검
# idpf 드라이버 로딩 확인
lsmod | grep idpf
ethtool -i eth1    # driver: idpf 확인

# virtchnl2 capability 협상 결과 확인
dmesg | grep -i "idpf.*cap\|idpf.*queue\|idpf.*split"

# 큐 설정 확인 (Split Queue 모델 여부)
ethtool -l eth1
ethtool -S eth1 | grep -E "tx_q|rx_q|comp_q|buf_q"

# 큐 수 변경
ethtool -L eth1 combined 8

# CP(Control Plane) 연결 상태 확인
dmesg | grep -i "idpf.*mailbox\|idpf.*cp\|idpf.*control"

# 기본 통계 확인
ethtool -S eth1 | head -30
ip -s link show eth1

ICE vs i40e: 아키텍처 진화

ICE(E810)는 i40e(XL710/X710)의 후속 세대로, AdminQ 기반 펌웨어 제어 모델을 계승하면서도 DDP 필수화, devlink 완전 통합, eswitch/representor 모델, 하드웨어 PTP 타임스탬프 등 근본적인 아키텍처 확장을 도입했습니다. 특히 DDP가 패킷 파서를 런타임에 재프로그래밍하는 모델은 i40e에서는 선택적이었지만, ICE에서는 정상 동작의 전제 조건이 되었습니다.

i40e → ICE 아키텍처 진화 i40e (XL710/X710) PF 드라이버 (i40e) AdminQ → FW RSS (64큐) virtchnl v1 VF DDP (선택적) 기본 ethtool 통계 미지원: eswitch, representor, devlink health 미지원: tc flower offload, HW PTP timestamp 진화 ICE (E810) PF 드라이버 (ice) AdminQ 확장 + devlink RSS (256큐) virtchnl v1/v2 + idpf DDP (필수) devlink health/flash 추가: eswitch (switchdev), representor netdev 추가: tc flower offload, HW PTP (ns), ADQ, LAG 100GbE (E810-CQDA2), 25GbE (E810-XXVDA2/4) 256 VF, GNSS 동기화, MACsec 일부 모델
i40e → ICE 아키텍처 진화: DDP 필수화, devlink/eswitch 통합, HW PTP, tc flower offload 추가
비교 항목i40e (XL710/X710)ICE (E810)진화 방향
최대 속도40GbE100GbE대역폭 2.5배 증가
RSS 큐최대 64최대 256병렬 처리 확대
SR-IOV VF최대 128최대 256가상화 밀도 증가
DDP선택적 확장필수 (Safe Mode fallback)HW 파서 유연성 극대화
devlink 지원기본 (info 정도)완전 (health, flash, param, port)통합 관리 인터페이스
eswitch미지원switchdev 모드OVS offload 지원
tc flower미지원HW offload 지원스위칭 규칙 HW 가속
PTPSW timestampHW timestamp (ns급)정밀 시간 동기화
Adaptive ITR지원지원 (4μs 단위)세밀한 코얼레싱
XDP5.1+5.5+성능 향상
RDMAiWARP (irdma)iWARP + RoCEv2 (irdma)프로토콜 범위 확장
마이그레이션 권장사항: i40e에서 ICE로 마이그레이션할 때 가장 주의해야 할 점은 DDP 패키지 배치입니다. ICE는 DDP 없이 Safe Mode로 동작하여 기존 i40e에서 당연히 사용하던 RSS, Flow Director, SR-IOV 고급 기능 등이 비활성화됩니다. 배포판의 linux-firmware 패키지가 최신인지 반드시 확인하세요.

ICE eswitch: Representor 모델과 Switchdev

ICE E810은 eswitch(Embedded Switch)를 통해 NIC 내부에 가상 스위치를 구성하고, 각 VF에 대응하는 representor netdev를 호스트에 노출합니다. 이 모델은 OVS(Open vSwitch) TC offload의 핵심 인프라이며, switchdev 모드로 전환하면 호스트의 TC subsystem이 NIC 하드웨어 필터 규칙을 직접 제어할 수 있습니다.

ICE eswitch Representor 흐름 Host Kernel PF netdev eth0 (ice) VF0 repr eth0_0 VF1 repr eth0_1 VFn repr eth0_n OVS Bridge / TC flower (sw + hw offload) Uplink repr enp1s0f0 NIC Hardware (E810) Embedded Switch (eswitch) MAC 학습 / VLAN 필터 / tc flower 규칙 HW 오프로드 VF0 port VF1 port VFn port PHY (Wire) tc flower 규칙 HW 오프로드
ICE eswitch representor 흐름: 호스트의 representor netdev → OVS/TC → NIC 내장 eswitch → VF/PHY 포트

Representor 모델은 NIC 내부의 각 VF 포트에 대응하는 호스트 측 netdev(representor)를 생성합니다. OVS나 TC는 이 representor를 일반 netdev처럼 다루면서 스위칭 규칙을 설정하고, ICE 드라이버가 해당 규칙을 NIC 하드웨어에 오프로드합니다. Slow path(매칭 실패) 패킷은 representor를 통해 호스트 OVS로 올라와 소프트웨어 처리됩니다.

eswitch 전환 시 주의: switchdev 모드로 전환하면 기존 VF 설정이 초기화되며, VF를 사용하는 VM이 일시적으로 연결이 끊길 수 있습니다. 전환 전에 반드시 모든 VF 트래픽이 중단된 상태에서 수행하세요. 또한 switchdev 모드에서는 ip link set vf 명령 대신 representor netdev를 통한 TC 규칙으로 VF 트래픽 정책을 관리해야 합니다.
# 실습 예제: ICE eswitch switchdev 모드 설정
# 1. 현재 eswitch 모드 확인
devlink dev eswitch show pci/0000:af:00.0
# 출력: pci/0000:af:00.0: mode legacy

# 2. VF 생성
echo 4 > /sys/class/net/eth0/device/sriov_numvfs

# 3. switchdev 모드 전환
devlink dev eswitch set pci/0000:af:00.0 mode switchdev
# representor netdev 생성 확인
ip link show | grep "eth0_"

# 4. OVS 브리지에 representor 연결
ovs-vsctl add-br br0
ovs-vsctl add-port br0 eth0_0   # VF0 representor
ovs-vsctl add-port br0 eth0_1   # VF1 representor
ovs-vsctl add-port br0 enp1s0f0 # Uplink representor

# 5. TC flower 규칙 추가 (HW 오프로드)
# VF0 → VF1 MAC 기반 포워딩
tc filter add dev eth0_0 ingress protocol ip \
  flower dst_mac aa:bb:cc:dd:ee:01 \
  action mirred egress redirect dev eth0_1

# 6. HW 오프로드 상태 확인
tc -s filter show dev eth0_0 ingress
# "in_hw" 플래그가 있으면 HW 오프로드 성공

# 7. eswitch 통계 확인
ethtool -S eth0 | grep "tx_repr\|rx_repr"
devlink dev eswitch show pci/0000:af:00.0

# 8. legacy 모드로 복원
ovs-vsctl del-br br0
devlink dev eswitch set pci/0000:af:00.0 mode legacy
OVS TC offload 최적화: ICE eswitch + OVS 환경에서 최대 성능을 얻으려면 OVS의 hw-offload 옵션을 활성화하고, tc-policy=none으로 설정하세요. 이렇게 하면 OVS가 첫 번째 패킷으로 플로우를 학습한 후, 이후 패킷은 NIC eswitch에서 하드웨어 수준으로 스위칭합니다. ovs-vsctl set Open_vSwitch . other_config:hw-offload=true other_config:tc-policy=none
eswitch 모드VF 제어 방식OVS 연동tc offload용도
legacy (기본)ip link set vf미지원미지원일반 SR-IOV 가상화
switchdevrepresentor + TC 규칙지원 (hw-offload)flower/u32SDN, 클라우드 오버레이
Representor netdev 이름 규칙: ICE eswitch에서 생성되는 representor netdev 이름은 PF netdev 이름을 기반으로 {pf_name}_{vf_index} 패턴을 따릅니다. 예를 들어 PF가 eth0이고 VF가 4개라면 eth0_0, eth0_1, eth0_2, eth0_3이 생성됩니다. Uplink representor는 물리 포트 이름(예: enp1s0f0)을 그대로 사용합니다.

eswitch switchdev 모드에서 지원하는 주요 tc flower 매칭 필드와 액션은 다음과 같습니다.

카테고리매칭 필드지원 액션
L2 src/dst MAC, VLAN ID, VLAN priority, EtherType redirect (포트 간 전달)
drop (패킷 드롭)
vlan push/pop (VLAN 태그 조작)
tunnel_key set/unset (터널 캡슐화)
ct (커넥션 트래킹, 제한적)
L3 src/dst IPv4, src/dst IPv6, IP proto, DSCP/ECN
L4 src/dst port (TCP/UDP), TCP flags
터널 VXLAN VNI, GENEVE VNI, GRE key
# 실습 예제: eswitch tc flower 고급 규칙
# VXLAN 터널 패킷을 VF0으로 스티어링
tc filter add dev enp1s0f0 ingress protocol ip \
  flower enc_dst_ip 10.0.0.1 enc_key_id 100 enc_dst_port 4789 \
  action tunnel_key unset \
  action mirred egress redirect dev eth0_0

# VF0 → 외부: VXLAN 캡슐화
tc filter add dev eth0_0 ingress protocol ip \
  flower src_mac aa:bb:cc:dd:ee:00 \
  action tunnel_key set id 100 src_ip 10.0.0.1 dst_ip 10.0.0.2 dst_port 4789 \
  action mirred egress redirect dev enp1s0f0

# VLAN 기반 트래픽 분리
tc filter add dev eth0_0 ingress protocol 802.1q \
  flower vlan_id 100 \
  action vlan pop \
  action mirred egress redirect dev eth0_1

# 오프로드된 규칙 수 확인
tc -s filter show dev eth0_0 ingress | grep "in_hw\|not_in_hw"
tc -s filter show dev enp1s0f0 ingress | grep "in_hw"

# devlink 포트 정보 확인
devlink port show pci/0000:af:00.0

# eswitch 장애 진단
devlink health show pci/0000:af:00.0
dmesg | grep -i "ice.*eswitch\|ice.*repr\|ice.*switchdev"
tc flower 규칙 수 제한: ICE E810의 eswitch에서 하드웨어 오프로드 가능한 tc flower 규칙 수는 NIC 내부 TCAM 크기에 의해 제한됩니다. 규칙 수가 한계를 초과하면 새 규칙은 not_in_hw로 표시되어 소프트웨어 경로로 처리됩니다. 대규모 규칙이 필요한 환경에서는 규칙의 우선순위를 세밀하게 관리하고, tc -s filter show로 HW 오프로드 상태를 주기적으로 모니터링하세요.
DDP 트러블슈팅 확장

ICE 드라이버가 DDP 패키지 로드에 실패하면 Safe Mode로 폴백하여 RSS, Flow Director, SR-IOV 고급 기능이 모두 비활성화된다. 주요 실패 원인과 해결 방법은 다음과 같다.

실패 원인증상 (dmesg)해결 방법
FW 버전 불일치ice: Failed to load DDP package, FW mismatchFW 업데이트: devlink dev flash pci/... file ice_pkg.bin
패키지 파일 손상ice: DDP invalid signaturelinux-firmware 패키지 재설치
패키지 파일 누락ice: Failed to open DDP package file/lib/firmware/intel/ice/ddp/ice.pkg 배치 확인
TCAM 진입 초과Flow Director 규칙 추가 실패규칙 수 줄이거나 SKU 업그레이드

E810 SKU별 TCAM 진입 한계: E810-C(100GbE, dual-port)는 TCAM 진입을 E810-XXV(25GbE)보다 더 많이 지원한다. E810-C는 FD(Flow Director) 진입 최대 512개 기본 설정인 반면, E810-XXV는 256개로 제한될 수 있다. ethtool -n eth0 rx-flow-hash tcp4ethtool --show-ntuple eth0으로 현재 사용 규칙 수와 최대 용량을 확인할 수 있다.

i40e AdminQ Timeout 복구

i40e 드라이버에서 AdminQ(Administration Queue) timeout은 PF와 펌웨어 간 명령 채널이 응답하지 않을 때 발생한다. 이 오류는 FW hang, PCI 전송 오류, 또는 과도한 AdminQ 명령 큐잉으로 발생한다.

AdminQ timeout 감지: i40e_aq_send_msg_to_vfI40E_AQ_RC_EAGAIN을 반환하거나 타임아웃 워크큐가 트리거되면 드라이버는 PF 리셋 시퀀스를 시작한다. PF 리셋이 완료되면 VF에게 VIRTCHNL_OP_RESET_VF 메시지를 전송하여 VF 드라이버(iavf)가 채널을 재설정하도록 지시한다.

# DDP 로드 상태 확인
dmesg | grep -E "ice.*ddp|ice.*package|ice.*safe mode"
ls -la /lib/firmware/intel/ice/ddp/

# AdminQ 오류 확인 (i40e)
dmesg | grep -E "i40e.*AdminQ|i40e.*AQ timeout|i40e.*reset"

# i40e 드라이버 통계에서 AdminQ 오류 카운터 확인
ethtool -S eth0 | grep -E "admin_queue|aq_"

# VF 리셋 이력 확인
dmesg | grep -E "iavf.*reset|i40e.*VF.*reset"

# i40e AdminQ 링 크기 조정 (모듈 파라미터)
modprobe i40e adminq_work_limit=64

# ice DDP 패키지 수동 지정
modprobe ice fw_name=intel/ice/ddp/ice-1.3.30.0.pkg

Intel 공통 성능 튜닝 체크리스트

Intel NIC 드라이버 세대와 관계없이 공통적으로 적용할 수 있는 성능 튜닝 포인트를 정리합니다. 특히 10GbE 이상의 고대역폭 환경에서는 커널 파라미터, IRQ affinity, 링 버퍼 크기 등을 체계적으로 점검해야 합니다.

튜닝 항목확인 명령권장 설정대상 드라이버
RX/TX 링 크기ethtool -g eth0최대값 (2048~4096)모든 드라이버
RSS 큐 수ethtool -l eth0물리 코어 수 이하igb, ixgbe, i40e, ice
IRQ affinitycat /proc/interrupts큐당 전용 CPU 코어멀티큐 드라이버 전체
Adaptive Coalescingethtool -c eth0워크로드별 조정모든 드라이버
GRO/TSOethtool -k eth0활성화 유지모든 드라이버
XPS (TX 큐 → CPU 매핑)cat /sys/class/net/eth0/queues/tx-*/xps_cpusNUMA 로컬 CPU멀티큐 드라이버 전체
Busy Pollingsysctl net.core.busy_poll50 (μs), 저지연 환경모든 드라이버
NUMA 친화cat /sys/class/net/eth0/device/numa_nodeNIC NUMA 노드에 프로세스 고정모든 드라이버
# Intel NIC 공통 성능 튜닝 스크립트
IFACE=eth0

# 링 버퍼 최대화
ethtool -G $IFACE rx 4096 tx 4096

# RSS 큐 수를 물리 코어 수에 맞춤
CORES=$(nproc --all)
ethtool -L $IFACE combined $CORES

# GRO, TSO, Checksum 오프로드 활성화 확인
ethtool -K $IFACE gro on tso on tx on rx on

# IRQ affinity 자동 설정 (irqbalance 대신 수동)
systemctl stop irqbalance
# Intel 제공 스크립트 또는 수동 설정
IRQS=$(grep $IFACE /proc/interrupts | awk '{print $1}' | tr -d ':')
CPU=0
for IRQ in $IRQS; do
  echo $CPU > /proc/irq/$IRQ/smp_affinity_list
  CPU=$((CPU + 1))
done

# XPS 설정 (TX 큐를 대응 CPU에 매핑)
for i in $(seq 0 $((CORES - 1))); do
  printf "%x" $((1 << i)) > /sys/class/net/$IFACE/queues/tx-$i/xps_cpus
done

# Busy Polling (저지연 환경)
sysctl -w net.core.busy_poll=50
sysctl -w net.core.busy_read=50

# NUMA 노드 확인 및 프로세스 고정
NUMA=$(cat /sys/class/net/$IFACE/device/numa_node)
echo "NIC NUMA node: $NUMA"
# numactl --cpunodebind=$NUMA --membind=$NUMA ./application
irqbalance vs 수동 affinity: 고성능 네트워킹 환경에서는 irqbalance 데몬의 자동 분배 대신 수동 IRQ affinity 설정을 권장합니다. irqbalance는 주기적으로 IRQ를 재배치하여 캐시 워밍을 깨뜨릴 수 있습니다. Intel의 set_irq_affinity.sh 스크립트를 사용하면 NUMA 토폴로지를 고려한 최적 매핑을 자동 계산합니다.

Intel NIC 드라이버 계열은 세대마다 설계 철학이 진화해 왔으며, 최신 커널에서는 ice + idpf 조합이 가장 포괄적인 기능 세트를 제공합니다. 기존 i40e/ixgbe 환경에서 업그레이드를 검토할 때는 DDP 패키지 배치, devlink 인프라 준비, SR-IOV VF 마이그레이션 계획을 사전에 수립하는 것이 중요합니다.

# Intel NIC 드라이버 세대별 빠른 점검 스크립트
# 시스템의 모든 Intel NIC 드라이버 요약
for dev in /sys/class/net/*/device/driver; do
  IFACE=$(echo "$dev" | cut -d'/' -f5)
  DRIVER=$(basename $(readlink "$dev"))
  case "$DRIVER" in
    e1000e|igb|ixgbe|i40e|ice|idpf|iavf)
      FW=$(ethtool -i "$IFACE" 2>/dev/null | grep firmware | awk '{print $2}')
      QUEUES=$(ethtool -l "$IFACE" 2>/dev/null | grep Combined | tail -1 | awk '{print $2}')
      echo "$IFACE: driver=$DRIVER fw=$FW queues=$QUEUES"
      ;;
  esac
done

# DDP 패키지 상태 확인 (ice 전용)
if lsmod | grep -q ice; then
  echo "=== ICE DDP Status ==="
  ls -la /lib/firmware/intel/ice/ddp/ 2>/dev/null
  dmesg | grep -i "ice.*ddp\|ice.*package" | tail -5
fi

# devlink 지원 드라이버 일괄 점검
devlink dev info 2>/dev/null | head -20

NVIDIA/Mellanox: mlx5e / mlx4_en

NVIDIA(구 Mellanox)의 ConnectX 시리즈는 데이터센터 네트워킹의 사실상 표준으로, 하드웨어 기반 RDMA, SR-IOV, 정밀 패킷 스티어링, XDP 제로카피 등 리눅스 커널의 최신 네트워킹 기능을 가장 적극적으로 지원하는 NIC 플랫폼이다. 커널 드라이버는 mlx5_core(통합 코어)와 mlx5e(이더넷), mlx5_ib(InfiniBand/RoCE) 모듈로 구성되며, 레거시 ConnectX-3 이하는 mlx4_core/mlx4_en으로 지원된다.

드라이버 지원 하드웨어 최대 속도 Max Queues XDP RDMA SR-IOV 커널 모듈 비고
mlx5e ConnectX-4 ~ ConnectX-7, BlueField-1/2/3 400 GbE 256 (per port) O (AF_XDP 포함) RoCEv2 / IB 1024 VFs mlx5_core, mlx5e 현재 권장 드라이버
mlx4_en ConnectX-2 ~ ConnectX-3 Pro 56 Gb/s IB, 40 GbE 64 O (기본) IB / RoCEv1 64 VFs mlx4_core, mlx4_en 레거시, 유지보수 모드

NVIDIA DOCA SDK와 커널 드라이버의 관계: DOCA(Data Center Infrastructure on Accelerated Computing) SDK는 BlueField DPU에서 사용자 공간 데이터 경로를 제공하지만, 기반 커널 드라이버는 여전히 mlx5_core이다. upstream 커널의 mlx5 드라이버는 DOCA 없이도 독립적으로 모든 기능을 제공한다.

mlx5 하드웨어 및 소프트웨어 아키텍처

mlx5 드라이버는 ConnectX-4 이상의 하드웨어를 지원하는 통합 코어 드라이버 mlx5_core를 기반으로 한다. 하드웨어 내부는 패킷 수신/발신 경로를 세밀하게 제어할 수 있는 스티어링 엔진, 가상 포트(vPort), 작업 큐(WQ) 체계로 구성되며, 소프트웨어 스택은 이더넷·RDMA·vDPA 등 다양한 서브시스템을 단일 코어 위에서 분기한다.

ConnectX 시리즈의 하드웨어 블록 구조는 다음과 같다. 물리 포트에서 수신된 패킷은 스티어링 엔진(Flow Steering Engine)을 거쳐 가상 포트(vPort)별로 분류되고, 각 vPort에 연결된 작업 큐(WQ)의 수신 큐(RQ) 또는 송신 큐(SQ)로 전달된다. 완료 이벤트는 CQ(Completion Queue)를 통해 EQ(Event Queue)로 전파되며, 최종적으로 MSI-X 인터럽트가 호스트 CPU에 전달된다.

mlx5 하드웨어 아키텍처 블록 다이어그램 Physical Port 0 25/50/100/200/400G Physical Port 1 Dual-Port NIC Steering Engine Flow Table Hierarchy Match + Action NIC / FDB / RDMA Header Rewrite, Encap vPort 0 (PF) Uplink vPort 1 (VF) SR-IOV VF vPort N (SF) Scalable Func SQ / RQ Work Queue WQE stride MPWQE Multi-Packet WQE Striding RQ CQ Completion Queue EQ Event Queue MSI-X CPU IRQ PCIe Gen4/5 x16 — DMA Engine — UAR (User Access Region) — HCA BAR Host Memory Ring Buffers (SQ/RQ) CQ/EQ Entries SKB / xdp_frame Page Pool (frag alloc) UMEM (AF_XDP) Firmware (FW) Command Interface HCA Capabilities Flow Steering Tables Health Monitoring Crypto / TLS offload Management FW devlink params FW Update (MCC) Health Reporters Port Config eSwitch Mgmt BlueField DPU Arm SoC (embedded) SmartNIC mode Separated Host OVS HW offload IPsec / TLS inline
그림 ndd-19. mlx5 하드웨어 아키텍처 블록 다이어그램 — 물리 포트에서 MSI-X 인터럽트까지의 데이터 흐름과 호스트 메모리, 펌웨어, 관리 평면의 상호작용

소프트웨어 스택은 mlx5_core가 HCA(Host Channel Adapter) 초기화, 커맨드 인터페이스, EQ/CQ 관리 등의 공통 기반을 제공하며, 그 위에 이더넷(mlx5e), InfiniBand/RoCE(mlx5_ib), vDPA(mlx5_vdpa) 서브시스템이 독립적으로 동작한다.

mlx5 소프트웨어 스택 계층 구조 User Space iproute2 / tc rdma-core / libibverbs DPDK mlx5 PMD libxdp / AF_XDP netdev subsystem net_device / ethtool / tc RDMA subsystem ib_core / ib_uverbs vDPA subsystem vhost / virtio devlink subsystem params / health / port mlx5e Ethernet / TC offload mlx5_ib RoCEv2 / IB verbs mlx5_vdpa vDPA net device devlink ops Health / FW mgmt mlx5_core Command Interface | EQ/CQ Mgmt | Page Alloc | HCA Init | eSwitch | FW Cmd Mailbox ConnectX HCA Firmware — PCIe BAR — DMA — IOMMU
그림 ndd-20. mlx5 소프트웨어 스택 — mlx5_core 통합 코어 위에 이더넷(mlx5e), RDMA(mlx5_ib), vDPA(mlx5_vdpa), devlink 서브시스템이 독립 동작

ConnectX 세대별 기능 진화는 다음 표와 같다. 각 세대는 대역폭뿐 아니라 하드웨어 오프로드 범위가 크게 확장되었다.

기능 ConnectX-5 ConnectX-6 ConnectX-6 Dx ConnectX-7
최대 속도 100 GbE (2x) 200 GbE (HDR IB) 100 GbE (2x) 400 GbE (NDR IB)
PCIe Gen 3.0 x16 Gen 3.0/4.0 x16 Gen 4.0 x16 Gen 5.0 x16
Crypto Offload - IPsec inline IPsec + TLS inline IPsec + TLS + MACsec
Steering 용량 4M rules 16M rules 16M rules 64M+ rules
XDP Native + AF_XDP Native + AF_XDP Native + AF_XDP + Multi-buf Native + AF_XDP + Multi-buf
GPUDirect RDMA RDMA + Storage RDMA + Storage RDMA + Storage + Network
eSwitch 향상 Switchdev 기본 Connection Tracking HW CT + NAT offload CT + NAT + Sample offload
Scalable Functions - - 지원 지원 (향상)
# mlx5 디바이스 정보 확인
lspci -d 15b3: -vvv | head -30
ethtool -i enp1s0f0np0

# mlx5 드라이버 로드 상태 및 파라미터 확인
modinfo mlx5_core
cat /sys/module/mlx5_core/parameters/prof_sel

# devlink 디바이스 정보
devlink dev show pci/0000:01:00.0
devlink dev info pci/0000:01:00.0

# HCA capabilities 확인 (디버깅용)
devlink dev param show pci/0000:01:00.0
rdma dev show mlx5_0

mlx5 Flow Steering 파이프라인

mlx5의 패킷 스티어링은 하드웨어 내장 Flow Table 계층 구조를 기반으로 동작한다. 세 가지 주요 도메인이 존재한다: NIC Flow Table(일반 NIC RX/TX 경로), FDB(Forwarding DataBase, eSwitch 경로), RDMA Flow Table(RDMA 트래픽 경로). 각 도메인 내에서 패킷은 우선순위 기반으로 Flow Table을 순차 탐색하며, 첫 번째 매칭 규칙의 액션이 수행된다.

NIC RX Flow Table은 일반적으로 다음 계층으로 구성된다: Bypass(tc flower offload 규칙) → Kernel(ethtool ntuple 등) → Leftovers(기본 RSS/steering). FDB는 eSwitch Switchdev 모드에서 활성화되며, VF/SF 간 패킷 전달과 OVS offload의 핵심이다.

mlx5 Flow Steering 파이프라인 Ingress Packet In NIC RX Flow Tables Bypass tc flower rules Kernel ethtool ntuple Leftovers RSS / Default VLAN 802.1Q filter TTC (Traffic Type Classifier) L3/L4 proto → RQ group FDB (eSwitch) Flow Tables Pre-ACL ingress filter TC offload match + action FDB Main L2 forwarding CT Table conntrack offload Post-ACL / Miss → SW Slow Path unmatched → representor netdev RX Queue (RQ) → NAPI poll Drop HW counter++ vPort (VF/SF) FDB forward Encap / Decap VXLAN / GRE HW Flow Counters packets / bytes per rule tc -s filter show dev ... ethtool -S (steering stats)
그림 ndd-21. mlx5 Flow Steering 파이프라인 — NIC RX와 FDB(eSwitch) 도메인의 Flow Table 계층 구조와 패킷 처리 경로

TC flower offload는 mlx5에서 가장 강력한 스티어링 기능 중 하나다. tc flower 필터를 skip_sw 플래그와 함께 추가하면 하드웨어 Flow Table에 직접 규칙이 삽입되며, 소프트웨어 경로를 완전히 우회한다.

# TC flower offload: 특정 IP의 트래픽을 VF representor로 리다이렉트
tc qdisc add dev enp1s0f0np0 ingress
tc filter add dev enp1s0f0np0 ingress protocol ip \
    flower skip_sw \
    src_ip 10.0.0.0/24 \
    action mirred egress redirect dev enp1s0f0np0_0

# VXLAN encap offload
tc filter add dev enp1s0f0np0_0 egress protocol ip \
    flower skip_sw \
    src_ip 192.168.1.0/24 \
    action tunnel_key set id 100 src_ip 10.0.0.1 dst_ip 10.0.0.2 \
    action mirred egress redirect dev vxlan_sys_4789

# Connection Tracking offload (CX-6+)
tc filter add dev enp1s0f0np0 ingress protocol ip \
    flower skip_sw ct_state +trk+est \
    action goto chain 1
tc filter add dev enp1s0f0np0 ingress protocol ip chain 1 \
    flower skip_sw \
    action ct commit \
    action mirred egress redirect dev enp1s0f0np0_0

# OVS HW offload 활성화 (OVS 2.8+)
ovs-vsctl set Open_vSwitch . other_config:hw-offload=true
ovs-vsctl set Open_vSwitch . other_config:tc-policy=skip_sw
systemctl restart openvswitch

# offload된 flow 통계 확인
tc -s filter show dev enp1s0f0np0 ingress

OVS offload 성능 팁: OVS HW offload를 사용할 때 tc-policy=skip_sw를 설정하면 모든 규칙이 하드웨어 우선으로 설치된다. Connection Tracking offload(CX-6 이상)를 함께 사용하면 stateful firewall 규칙까지 하드웨어에서 처리되어 10M+ flows/sec 수준의 성능을 달성할 수 있다.

mlx5 eSwitch: Legacy vs Switchdev 모드

mlx5 eSwitch는 NIC 내부의 가상 스위치로, SR-IOV VF와 PF 간의 트래픽을 관리한다. 두 가지 동작 모드가 있다: Legacy 모드는 전통적인 MAC/VLAN 기반 L2 스위칭을 제공하며, Switchdev 모드는 각 VF/SF에 대한 representor netdev를 생성하여 TC/OVS 기반의 정밀한 프로그래머블 데이터 경로를 제공한다.

mlx5 eSwitch Legacy vs Switchdev 모드 비교 Legacy Mode PF netdev enp1s0f0np0 VF0 VM/Container VF1 VM/Container VF2 VM/Container eSwitch (Legacy) MAC/VLAN 기반 L2 스위칭 제한된 필터링 (ip link set vf) NIC HW (Port) Wire TC offload 불가, OVS HW offload 불가, 제한적 QoS Switchdev Mode Uplink Rep enp1s0f0np0 (PF) VF0 Rep eth0 (repr) VF1 Rep eth1 (repr) SF Rep en3f0pf0sf8 OVS Bridge / TC flower (slow path) 소프트웨어 판단 → 규칙 하드웨어 설치 FDB (HW fast path) TC offload / CT offload / Encap-Decap Match+Action → vPort 직접 전달 NIC HW (Port) Wire TC/OVS offload, CT offload, 프로그래머블 FDB, SF 지원
그림 ndd-22. mlx5 eSwitch Legacy vs Switchdev 모드 비교 — Switchdev 모드에서 representor netdev와 FDB 기반 하드웨어 fast path 제공

eSwitch 모드 전환 시에는 기존 VF 설정이 초기화되므로, 운영 중인 VM의 네트워크 연결이 일시적으로 끊어질 수 있다. 사전에 VF를 unbind하고 전환 후 다시 bind하는 절차가 필요하다.

# 현재 eSwitch 모드 확인
devlink dev eswitch show pci/0000:01:00.0

# SR-IOV VF 생성
echo 4 > /sys/class/net/enp1s0f0np0/device/sriov_numvfs

# Switchdev 모드로 전환 (VF unbind 필요)
echo 0000:01:00.2 > /sys/bus/pci/drivers/mlx5_core/unbind
echo 0000:01:00.3 > /sys/bus/pci/drivers/mlx5_core/unbind
devlink dev eswitch set pci/0000:01:00.0 mode switchdev
echo 0000:01:00.2 > /sys/bus/pci/drivers/mlx5_core/bind
echo 0000:01:00.3 > /sys/bus/pci/drivers/mlx5_core/bind

# Representor 디바이스 확인
ip link show type mlx5_rep 2>/dev/null || ip link show | grep repr

# OVS bridge에 representor 추가
ovs-vsctl add-br br-int
ovs-vsctl add-port br-int enp1s0f0np0     # uplink representor
ovs-vsctl add-port br-int enp1s0f0np0_0   # VF0 representor
ovs-vsctl add-port br-int enp1s0f0np0_1   # VF1 representor

# Scalable Functions (SF) 생성 (CX-6 Dx+)
devlink port add pci/0000:01:00.0 flavour pcisf pfnum 0 sfnum 8
devlink port function set pci/0000:01:00.0/32768 \
    hw_addr 00:11:22:33:44:55 state active

# Legacy 모드로 복귀
devlink dev eswitch set pci/0000:01:00.0 mode legacy

Switchdev 모드 전환 주의사항: 모드 전환은 NIC의 모든 스티어링 규칙을 초기화한다. 운영 환경에서는 반드시 유지보수 윈도우에서 수행해야 하며, VF가 할당된 모든 VM/컨테이너의 네트워크가 일시 중단된다. 또한 일부 ConnectX-5 초기 펌웨어는 Switchdev → Legacy 복귀 시 NIC 리셋이 필요할 수 있다.

mlx5e XDP 및 AF_XDP 제로카피

mlx5e는 MPWQE(Multi-Packet Work Queue Entry) 기반의 효율적인 XDP 경로를 제공한다. MPWQE는 하나의 WQE에 여러 패킷을 담는 Striding RQ 방식으로, 메모리 효율과 캐시 친화성이 높다. XDP 프로그램은 RQ에서 패킷을 수신한 직후, SKB 할당 전에 실행되어 XDP_DROP, XDP_TX, XDP_REDIRECT, XDP_PASS 결정을 내린다.

mlx5e XDP 데이터 경로 NIC HW DMA to Host Mem RSS → RQ MPWQE RQ Striding RQ page_pool frag xdp_buff 생성 CQE → NAPI XDP Program bpf_prog_run_xdp() xdp_buff → verdict multi-buffer 지원 XDP_DROP page recycle XDP_TX same port SQ XDP_REDIRECT other dev / CPU XDP_PASS → SKB alloc AF_XDP Zero-Copy Path UMEM (user mem) XSK RQ (dedicated) XSK CQ → user space DMA 직접 → UMEM 프레임 → XDP_REDIRECT → xsk_sendmsg() (TX) UMEM bound XDP TX SQ 전용 SQ (per channel) doorbell batching inline WQE (small pkt)
그림 ndd-23. mlx5e XDP 데이터 경로 — MPWQE Striding RQ에서 XDP 프로그램 실행까지의 흐름과 AF_XDP 제로카피 경로

AF_XDP 제로카피 모드에서 mlx5e는 UMEM(User Memory)을 직접 RQ에 매핑하여 커널 버퍼 복사 없이 사용자 공간으로 패킷을 전달한다. 아래는 세대별 AF_XDP 성능 데이터이다.

시나리오 CX-5 100G CX-6 Dx 100G CX-7 200G 비고
XDP_DROP (single core) ~24 Mpps ~28 Mpps ~35 Mpps 64B 패킷
XDP_TX (single core) ~18 Mpps ~22 Mpps ~28 Mpps loopback 모드
AF_XDP RX zero-copy ~12 Mpps ~16 Mpps ~22 Mpps 1 socket, 1 queue
AF_XDP TX zero-copy ~10 Mpps ~14 Mpps ~19 Mpps 1 socket, 1 queue
AF_XDP bidi zero-copy ~8 Mpps ~12 Mpps ~16 Mpps RX+TX 동시
# XDP 프로그램 로드 (native 모드)
ip link set dev enp1s0f0np0 xdp obj xdp_prog.o sec xdp

# XDP 프로그램 상태 확인
ip link show dev enp1s0f0np0
bpftool prog show
bpftool net show dev enp1s0f0np0

# AF_XDP 소켓 성능 테스트 (xdp-tools)
xdp-bench rx enp1s0f0np0 -m native
xsk_fwd -i enp1s0f0np0 -q 0 -z    # zero-copy 모드

# XDP 통계 확인
ethtool -S enp1s0f0np0 | grep xdp
# rx_xdp_drop, rx_xdp_redirect, rx_xdp_tx, rx_xdp_tx_full 등

# Multi-buffer XDP (CX-6 Dx+, jumbo frame 지원)
ip link set dev enp1s0f0np0 mtu 9000
ip link set dev enp1s0f0np0 xdp obj xdp_mb_prog.o sec xdp

# XDP 프로그램 해제
ip link set dev enp1s0f0np0 xdp off

AF_XDP 성능 최적화: 최상의 성능을 위해 (1) IRQ 어피니티와 XSK 소켓을 동일 CPU에 배치하고, (2) busy_poll을 활성화하며(setsockopt SO_BUSY_POLL), (3) UMEM 프레임 크기를 2048 또는 4096으로 설정한다. mlx5e는 XDP_USE_NEED_WAKEUP 플래그도 지원하여 불필요한 시스템콜을 줄인다.

mlx5 RDMA: RoCEv2 / GPUDirect / ODP

mlx5_ib 모듈은 ConnectX NIC의 RDMA 기능을 IB verbs 인터페이스로 노출한다. RoCEv2(RDMA over Converged Ethernet v2)는 UDP/IP 캡슐화를 사용하여 표준 이더넷 스위치 인프라 위에서 RDMA를 제공하며, GPUDirect RDMA는 GPU 메모리에 대한 직접 DMA를 지원하여 AI/HPC 워크로드에서 핵심 역할을 한다.

기능 설명 커널 요구사항 최소 HW
RoCEv2 UDP/IP 기반 RDMA, ECN/PFC QoS 연동 mlx5_ib, rdma-core CX-4
ODP (On-Demand Paging) MR 등록 없이 가상 메모리 직접 사용, page fault 처리 CONFIG_INFINIBAND_ON_DEMAND_PAGING CX-4
GPUDirect RDMA GPU 메모리 ↔ NIC 직접 DMA (nvidia-peermem) nvidia-peermem 모듈 CX-5
GPUDirect Storage NVMe ↔ GPU 메모리 직접 전송 nvidia-fs 모듈 CX-6
Adaptive Routing 네트워크 혼잡 시 자동 경로 변경 FW 지원 필요 CX-7
Device Memory (DM) NIC 온보드 메모리에 QP 컨텍스트 저장 mlx5_ib DM API CX-5
Crypto MR 하드웨어 암호화된 RDMA 전송 FW 지원 필요 CX-6 Dx
# RDMA 디바이스 및 포트 상태 확인
rdma dev show
rdma link show mlx5_0/1
ibstat mlx5_0

# RoCEv2 모드 설정 (기본값)
cma_roce_mode -d mlx5_0 -p 1 -m 2    # RoCE v2

# ECN 설정 (DCQCN)
mlnx_qos -i enp1s0f0np0 --trust dscp
mlnx_qos -i enp1s0f0np0 --pfc 0,0,0,1,0,0,0,0  # TC3에 PFC

# GPUDirect RDMA (nvidia-peermem 로드)
modprobe nvidia-peermem
cat /sys/module/nvidia_peermem/version

# RDMA 대역폭 테스트
ib_write_bw -d mlx5_0 -x 3 --report_gbits    # server
ib_write_bw -d mlx5_0 -x 3 --report_gbits 10.0.0.1  # client

# ODP (On-Demand Paging) 확인
rdma dev show mlx5_0 -j | jq '.[] | .caps'
RoCEv2와 Ethernet 드라이버 상호작용 심화

RoCEv2 트래픽은 mlx5_ib가 생성하지만, 실제 패킷 전송은 mlx5e(Ethernet 드라이버)의 SQ(Send Queue)를 공유한다. CQ(Completion Queue)와 EQ(Event Queue) 인프라도 Ethernet과 RDMA 경로가 공유하므로, RDMA 워크로드가 CQ를 고갈시키면 Ethernet 트래픽에도 영향이 미칠 수 있다.

PFC(Priority Flow Control)와 ECN(Explicit Congestion Notification)은 RoCEv2 환경에서 필수다. RoCEv2는 패킷 드롭에 취약하므로(TCP의 재전송과 달리 RDMA는 재전송 메커니즘이 제한적), lossless 패브릭 구성이 요구된다. PFC는 특정 우선순위 큐에서 혼잡이 발생할 때 해당 우선순위 트래픽만 일시 정지시켜 패킷 드롭 없이 버퍼 압력을 완화한다. ECN(DCQCN 알고리즘)은 네트워크 혼잡이 시작될 때 송신 속도를 선제적으로 낮춘다.

DSCP 기반 우선순위 매핑: mlx5는 RoCEv2 패킷에 특정 DSCP 값을 부여하고, 스위치와 NIC가 해당 DSCP를 TC(Traffic Class)로 매핑하여 PFC와 ECN을 적용한다. 일반적으로 RoCEv2는 DSCP 26(CS3) 또는 DSCP 46(EF)을 사용하며, mlnx_qos 도구로 NIC 측 매핑을 설정한다.

# RoCEv2 설정 검증 명령어

# 1. RoCE 모드 확인 (v2 = 2)
cma_roce_mode -d mlx5_0 -p 1
# port 1 RoCE mode: RoCE v2

# 2. PFC 설정 확인 (TC3에 PFC 적용 예시)
mlnx_qos -i enp1s0f0np0 --pfc
# pfc: 0,0,0,1,0,0,0,0  (TC3만 lossless)

# 3. DSCP → 우선순위 매핑 확인
mlnx_qos -i enp1s0f0np0 --trust dscp
mlnx_qos -i enp1s0f0np0 --dscp2prio

# 4. ECN 설정 확인
cat /sys/kernel/debug/mlx5/0000:*/cc_params/cc_algo 2>/dev/null
# DCQCN이 활성화되어 있어야 함

# 5. CQ/EQ 인프라 공유 상태 확인
rdma stat show mlx5_0
ethtool -S enp1s0f0np0 | grep -E "rx_cq_|tx_cq_"

# 6. RoCE 트래픽 카운터 확인
rdma stat show -j mlx5_0 | jq '.[] | select(.type=="mr")'
perfquery -d mlx5_0 -x 3 -l    # 포트 카운터 (패킷 드롭 확인)
RoCEv2 lossless 패브릭 요구사항: PFC와 ECN 없이 RoCEv2를 운영하면 네트워크 혼잡 시 RDMA 연결이 끊기고 복구 불가능한 상태가 될 수 있다. 스위치와 NIC 양쪽에서 PFC가 일치하지 않으면 pause storm이 발생할 수 있으므로, 엔드투엔드 구성 검증이 필수다.

mlx5 드라이버는 devlink health 프레임워크를 통해 하드웨어 오류 감지, 진단, 자동 복구를 제공한다. 각 reporter는 특정 하드웨어/소프트웨어 구성 요소를 모니터링하며, 오류 발생 시 진단 정보(dump)를 수집하고 자동 복구를 시도한다.

mlx5 devlink health 복구 흐름 HW Event FW syndrome CQ/EQ error Reporter fw / fw_fatal tx / rx vnic Diagnose 상태 조회 카운터 수집 FW health buf Dump CR-space dump FW trace log SQ/RQ state Recover SQ/RQ reset FW reset Full HCA reset Verify health OK? link up? 복구 실패 시 재시도 (grace period 후) auto_recover: true | grace_period: 60s | auto_dump: true | devlink health 관리
그림 ndd-24. mlx5 devlink health 복구 흐름 — 하드웨어 이벤트 감지에서 자동 복구까지의 파이프라인
Reporter 모니터링 대상 Dump 내용 복구 방법
fw FW 비정상 syndrome FW health buffer, syndrome code FW 커맨드 인터페이스 리셋
fw_fatal FW 치명적 오류 (assert/crash) CR-space 전체 덤프, FW trace PCI reset / full HCA reset
tx TX timeout, SQ 에러 SQ 상태, WQE 내용, CQ 상태 SQ reset + 재시작
rx RQ 에러, RX timeout RQ 상태, ICOSQ 상태 RQ/channel 재시작
vnic vNIC 진단 카운터 TX/RX queue counter, total errors 진단 전용 (복구 없음)
# Health reporter 목록 및 상태 확인
devlink health show pci/0000:01:00.0

# 특정 reporter 상세 정보
devlink health show pci/0000:01:00.0 reporter fw

# 진단 정보 조회
devlink health diagnose pci/0000:01:00.0 reporter fw
devlink health diagnose pci/0000:01:00.0 reporter vnic

# 덤프 수집 (FW 오류 분석용)
devlink health dump show pci/0000:01:00.0 reporter fw_fatal

# 수동 복구 트리거
devlink health recover pci/0000:01:00.0 reporter tx

# Auto-recovery 설정
devlink health set pci/0000:01:00.0 reporter fw \
    auto_recover true grace_period 60000
devlink health set pci/0000:01:00.0 reporter fw_fatal \
    auto_dump true

# Health 이벤트 모니터링 (실시간)
devlink health -j monitor

fw_fatal reporter와 PCI reset: fw_fatal reporter의 복구는 PCI-level reset을 수행하므로, 해당 NIC에 연결된 모든 네트워크 인터페이스와 VF가 일시적으로 중단된다. grace_period를 적절히 설정하여 빈번한 리셋 루프를 방지해야 한다. 기본값은 60초이다.

mlx5 펌웨어 관리

ConnectX NIC의 펌웨어 업데이트는 devlink 프레임워크를 통해 수행된다. 업데이트 프로세스는 FW 이미지 다운로드, 무결성 검증, 플래시 기록, 활성화의 단계로 진행되며, flash_only 옵션을 사용하면 NIC 리셋 없이 다음 부팅 시 적용할 수 있다.

mlx5 펌웨어 업데이트 흐름 1. Download .mfa2 이미지 devlink flash MCC 커맨드 2. Verify SHA-256 검증 디지털 서명 HW 호환성 3. Flash SPI Flash 기록 진행률 표시 ~2-5분 소요 4. Activate FW reset cmd 또는 flash_only (다음 부팅 적용) 5. Reset PCI reset 또는 시스템 리부팅 검증 실패 → 중단 Live Patch (CX-6+, FW 지원 시) devlink dev flash ... overwrite settings 리셋 없이 FW 핫 업그레이드 (제한적 지원)
그림 ndd-25. mlx5 펌웨어 업데이트 흐름 — 이미지 다운로드에서 활성화까지의 단계별 과정
# 현재 FW 버전 확인
devlink dev info pci/0000:01:00.0
ethtool -i enp1s0f0np0 | grep firmware

# FW 업데이트 (즉시 활성화)
devlink dev flash pci/0000:01:00.0 \
    file fw-ConnectX7-rel-28_39_1002-MCX713106AS-VEA_Ax-UEFI-14.32.17-FlexBoot-3.7.300.bin

# FW 업데이트 (다음 리부팅 시 적용)
devlink dev flash pci/0000:01:00.0 \
    file firmware.mfa2 component fw \
    overwrite settings

# FW reset (live reset, 서비스 중단 최소화)
devlink dev reload pci/0000:01:00.0 action fw_activate

# 설정 보존 여부 확인
mlxconfig -d /dev/mst/mt4129_pciconf0 query

# NIC 설정 변경 (mlxconfig, OFED 도구)
mlxconfig -d /dev/mst/mt4129_pciconf0 set SRIOV_EN=1 NUM_OF_VFS=16

펌웨어 업데이트 시 주의: (1) 업데이트 중 전원 차단 시 NIC가 복구 불가능(brick)해질 수 있으므로 UPS 환경에서 수행한다. (2) overwrite settings 플래그 없이 업데이트하면 기존 NIC 설정(SR-IOV, UEFI 부팅 등)이 공장 초기값으로 리셋될 수 있다. (3) 듀얼 포트 NIC는 한쪽 포트로만 업데이트하면 된다(FW는 공유).

mlx5 최대 강점: 타 드라이버 비교 심층 분석

mlx5e는 단일 드라이버 안에서 가장 넓은 범위의 커널 네트워킹 기능을 프로덕션 수준으로 지원하는 유일한 NIC 드라이버이다. 여기서는 mlx5의 핵심 강점을 다른 드라이버와 정량적·구조적으로 비교하여, 왜 데이터센터 표준이 되었는지를 분석한다.

mlx5e 핵심 강점 — 타 드라이버 비교 1. Flow Steering 용량 mlx5e: 64M+ rules (CX-7) ice: 16K TCAM + DDP profiles bnxt: 8K EM + 2K TCAM ena/gve: HW steering 없음 cxgb4: 2K filter entries mlx5 대비 400~4000배 격차 2. XDP / AF_XDP 성능 mlx5e: 35 Mpps DROP (CX-7) ice: ~25 Mpps (E810 100G) bnxt: ~20 Mpps (BCM57508) i40e: ~18 Mpps (X710) r8169: XDP 미지원 AF_XDP ZC: mlx5/ice/i40e만 지원 3. 오프로드 범위 mlx5e: TC+CT+NAT+VXLAN+IPsec+TLS bnxt: TC+CT+VXLAN (TruFlow) ice: TC+VXLAN+IPsec (제한적 CT) cxgb4: TOE+TLS+IPsec (다른 모델) ena/gve: 오프로드 미지원 유일한 full-stack HW offload 4. 가상화 확장성 mlx5: 1024 VFs + SF (무제한) ice: 256 VFs, SF 없음 bnxt: 256 VFs, SF 없음 mlx4: 64 VFs, SF 없음 ena/gve: SR-IOV 미지원 (PV 모델) SF: mlx5 독점 기능 (CX-6 Dx+) 5. RDMA / GPUDirect mlx5: RoCEv2+IB+GPUDirect+ODP bnxt: RoCEv2 (제한적) cxgb4: iWARP (RoCE 미지원) ice: 별도 irdma (iWARP+RoCE) 기타: RDMA 미지원 AI/HPC 워크로드: mlx5 사실상 독점 6. 관측성 / 관리 mlx5: devlink 5개 reporter bnxt: devlink 3개 reporter ice: devlink 2개 reporter ena: CloudWatch 메트릭 의존 r8169: ethtool -S 기본만 FW live update: mlx5만 지원 결론: mlx5e는 6개 핵심 영역 모두에서 최고 수준 — 유일한 "전 영역 리더" NIC 드라이버 범례: mlx5e (NVIDIA/Mellanox) ice (Intel E810) bnxt_en (Broadcom) cxgb4 (Chelsio) 클라우드/소비자 NIC ※ 성능 수치는 단일 코어, 64B 패킷 기준. 실제 성능은 CPU, PCIe 세대, FW 버전, 커널 버전에 따라 변동
그림 ndd-71. mlx5e 핵심 강점 비교 — 6개 카테고리에서 타 드라이버 대비 우위 분석
강점 1: Flow Steering — 압도적 규칙 용량과 유연성

mlx5의 하드웨어 Flow Steering Engine은 64M+ 이상의 flow rules을 지원한다(ConnectX-7 기준). 이는 경쟁 드라이버 대비 수백~수천 배의 격차이다. 단순한 숫자 차이를 넘어, Flow Table 계층 구조의 아키텍처적 유연성이 핵심 차별점이다.

드라이버 HW Flow Rules 용량 매치 필드 수 액션 체이닝 CT Offload 동적 규칙 추가 규칙 삽입 속도
mlx5e 64M+ (EM+TCAM) 200+ 다단계 goto chain HW CT+NAT O (무중단) ~100K rules/sec
ice 16K (TCAM) + DDP 확장 ~40 단일 액션 제한적 O ~10K rules/sec
bnxt_en 8K EM + 2K TCAM ~30 TruFlow 체이닝 HW CT O ~20K rules/sec
cxgb4 2K filter entries ~15 단일 액션 미지원 O ~5K rules/sec
ena 없음 (SW만) - - - - -
r8169 없음 - - - - -

왜 mlx5가 이렇게 큰 용량을 제공하는가?

# mlx5 Flow Steering 용량 확인
devlink resource show pci/0000:01:00.0

# 현재 HW offloaded flow 수 확인
tc -s filter show dev enp1s0f0np0 ingress | grep -c "in_hw"

# OVS HW offload 시 flow 통계
ovs-appctl dpctl/dump-flows type=offloaded | wc -l

# 비교: ice 드라이버의 제한적 steering
# ice는 tc flower 지원하지만, 16K entries 이후 SW fallback
ethtool --show-priv-flags enp2s0f0 | grep flow-director
실전 영향: Kubernetes 환경에서 수천 개의 Pod에 대한 네트워크 정책을 HW offload할 때, mlx5는 64M+ rules로 충분한 여유가 있지만, ice(16K)나 bnxt(10K)는 빠르게 한계에 도달하여 SW fallback이 발생한다. 10K+ Pod 클러스터에서는 사실상 mlx5만 완전한 HW offload가 가능하다.
강점 2: XDP/AF_XDP — 최고 성능과 가장 완전한 구현

mlx5e의 XDP 구현은 커널 커뮤니티에서 참조 구현(reference implementation)으로 간주된다. MPWQE(Multi-Packet Work Queue Entry) 기반 Striding RQ가 핵심 차별점으로, 하나의 WQE에 여러 패킷을 배치하여 descriptor 오버헤드를 최소화한다.

기능 mlx5e ice bnxt_en i40e virtio_net r8169
XDP Native ✓ (모든 CX-4+)
AF_XDP Zero-Copy ✓ (CX-5+)
XDP Multi-buffer ✓ (CX-6 Dx+)
XDP_REDIRECT page ref ✓ (page_pool) ✗ (copy) -
XDP_DROP 성능 (1코어) ~35 Mpps ~25 Mpps ~20 Mpps ~18 Mpps ~5 Mpps -
AF_XDP RX ZC (1코어) ~22 Mpps ~16 Mpps - ~12 Mpps - -

mlx5 XDP 성능의 비밀: MPWQE Striding RQ

# mlx5 Striding RQ 모드 확인
ethtool --show-priv-flags enp1s0f0np0 | grep striding
# rx_striding_rq: on  ← MPWQE 활성화

# XDP 성능 벤치마크 (mlx5 vs ice)
ethtool -G enp1s0f0np0 rx 8192
ethtool -C enp1s0f0np0 rx-usecs 0
xdp-bench drop enp1s0f0np0 -m native    # mlx5: ~35 Mpps
xdp-bench drop enp2s0f0 -m native       # ice: ~25 Mpps

# AF_XDP 제로카피 비교
xsk_fwd -i enp1s0f0np0 -q 0 -z -p      # mlx5: ~22 Mpps
xsk_fwd -i enp2s0f0 -q 0 -z -p         # ice: ~16 Mpps

# bpftrace: XDP 경로 지연 측정
bpftrace -e 'kprobe:mlx5e_xdp_handle { @start[tid] = nsecs; }
kretprobe:mlx5e_xdp_handle /@start[tid]/ {
    @ns = hist(nsecs - @start[tid]); delete(@start[tid]);
}'
강점 3: 오프로드 범위 — 유일한 Full-Stack HW Offload

mlx5e는 커널 네트워킹 스택의 거의 모든 기능을 하드웨어로 오프로드할 수 있는 유일한 드라이버이다. TC flower + Connection Tracking + NAT + 터널(VXLAN/GRE/Geneve) + IPsec + kTLS + MACsec를 동시에 하드웨어에서 처리한다.

오프로드 기능 mlx5e ice bnxt_en cxgb4 ena
TC flower (skip_sw) ✓ 200+ 필드 ✓ ~40 필드 ✓ ~30 필드 ✓ ~15 필드
Connection Tracking ✓ HW CT+NAT 제한적 ✓ HW CT
VXLAN/GRE/Geneve Encap ✓ HW encap/decap
IPsec Inline ✓ (CX-6+) ✓ (E810) ✓ (T6)
kTLS TX/RX Inline ✓ (CX-6 Dx+) ✓ (T6)
MACsec ✓ (CX-7)
OVS HW Offload ✓ (mature) ✓ (제한적)
HW Timestamping (PTP) ✓ (ns 정밀도) ✓ (ns 정밀도)
Header Rewrite ✓ (MAC/IP/port) 제한적
Packet Sampling (sFlow) ✓ (CX-7)

클라우드 네이티브 환경에서 패킷은 여러 처리 단계를 거친다: OVS/eBPF 매칭 → VXLAN 캡슐화 → Connection Tracking → NAT 변환 → IPsec 암호화. mlx5는 이 전체 파이프라인을 HW에서 처리하여 CPU를 완전히 해방한다. 다른 드라이버는 일부 단계에서 SW fallback이 발생하여 CPU 오버헤드가 급증한다.

kTLS offload 성능: mlx5e의 kTLS inline 암호화는 AES-GCM-256을 line rate로 처리한다. 100 GbE에서 SW kTLS는 CPU 8~12코어를 소모하지만, mlx5 HW offload 시 CPU 사용량이 거의 0에 수렴한다. 이는 HTTPS가 대부분인 현대 웹서비스에서 막대한 TCO 절감을 의미한다.
강점 4: 가상화 — SR-IOV + Scalable Functions

mlx5는 전통적 SR-IOV(1024 VFs)에 더해 Scalable Functions(SF)라는 고유한 기능을 제공한다. SF는 VF와 달리 PCIe function을 소비하지 않으므로, 하드웨어 PCIe 한계(통상 256 functions)를 초월하여 수천 개의 독립 네트워크 기능을 생성할 수 있다.

특성 mlx5 VF mlx5 SF ice VF bnxt VF ena (PV)
최대 수 1024 제한 없음 (수천) 256 256 N/A (PV)
PCIe function 소비 1 per VF 0 (소프트웨어) 1 per VF 1 per VF N/A
독립 netdev
독립 RDMA dev
eSwitch representor ✓ (Switchdev) ✓ (Switchdev)
독립 devlink 인스턴스
동적 생성/삭제 재부팅 필요 ✓ (무중단) 재부팅 필요 재부팅 필요 N/A
# SR-IOV VF 생성
echo 16 > /sys/bus/pci/devices/0000:01:00.0/sriov_numvfs

# Scalable Function 생성 (mlx5 전용, CX-6 Dx+)
devlink port add pci/0000:01:00.0 flavour pcisf pfnum 0 sfnum 88
devlink port function set pci/0000:01:00.0/32768 \
    hw_addr 00:11:22:33:44:55 state active

# SF의 독립 devlink 인스턴스 확인
devlink dev show
# pci/0000:01:00.0  ← PF
# auxiliary/mlx5_core.sf.4  ← SF 독립 인스턴스

# 비교: ice VF 생성 (최대 256, SF 미지원)
echo 256 > /sys/bus/pci/devices/0000:02:00.0/sriov_numvfs
SF vs VF — 언제 SF를 사용하는가? SF는 컨테이너 네트워킹(각 Pod에 전용 NIC 리소스), microservice isolation, DPU(BlueField)에서의 서비스 체이닝에 이상적이다. VF와 달리 재부팅 없이 동적 생성/삭제가 가능하고, 각 SF가 독립 devlink/RDMA 인스턴스를 갖기 때문에 세밀한 관리와 모니터링이 가능하다.
강점 5: RDMA/GPUDirect — AI/HPC 독점적 지위

AI 훈련과 HPC 워크로드에서 mlx5는 사실상 유일한 선택지이다. RoCEv2, InfiniBand, GPUDirect RDMA, GPUDirect Storage, On-Demand Paging(ODP)을 모두 단일 NIC에서 제공하는 드라이버는 mlx5뿐이다.

RDMA 기능 mlx5 (RoCEv2/IB) bnxt (RoCEv2) ice/irdma cxgb4 (iWARP)
프로토콜 RoCEv2 + InfiniBand RoCEv2만 iWARP + RoCEv2 iWARP만
최대 대역폭 400 Gbps (NDR) 200 Gbps 100 Gbps 100 Gbps
GPUDirect RDMA ✓ (모든 GPU)
GPUDirect Storage ✓ (CX-6+)
ODP (On-Demand Paging)
Adaptive Routing ✓ (CX-7)
NCCL SHARP 가속 ✓ (IB 전용)
Multi-QP 동시 사용 수백만 QP 수만 QP 수천 QP 수천 QP
GPUDirect RDMA vs 전통적 전송 경로 전통적 경로 (3단계 복사) GPU Memory VRAM (HBM) copy① CPU Memory DRAM bounce copy② NIC TX DMA ring Network CPU 오버헤드: 높음 대역폭: PCIe 병목 GPUDirect RDMA (mlx5 전용) GPU Memory VRAM (HBM) P2P DMA (nvidia-peermem) CPU 바이패스 mlx5 NIC ConnectX HCA Network CPU 오버헤드: 0 대역폭: line rate 성능 비교 (ConnectX-7, A100 GPU, PCIe Gen5) 전통적: ~50 GB/s CPU 2코어 점유 GPUDirect: ~400 Gbps wire CPU 0코어 점유 SHARP (IB): all-reduce 2x 스위치 내 in-network 연산
그림 ndd-72. GPUDirect RDMA vs 전통적 전송 경로 — CPU 바이패스로 GPU↔NIC 직접 DMA, mlx5 ConnectX만 지원
# GPUDirect RDMA 환경 확인
nvidia-smi topo -m
cat /proc/driver/nvidia-peermem/version

# GPUDirect RDMA 대역폭 테스트
ib_write_bw -d mlx5_0 --use_cuda=0 -x 3 --report_gbits

# NCCL all-reduce 벤치마크 (multi-GPU, SHARP 가속)
nccl-tests/build/all_reduce_perf -b 1M -e 1G -g 8
AI 훈련 네트워크에서 mlx5가 필수인 이유: NVIDIA NCCL은 mlx5 InfiniBand에서 SHARP(Scalable Hierarchical Aggregation and Reduction Protocol)를 통해 스위치 내에서 all-reduce를 HW 가속한다. GPU 수가 많을수록 통신 오버헤드가 기하급수적으로 줄어드는 효과를 제공하며, 다른 NIC에서는 원천적으로 불가능한 기능이다.
강점 6: 관측성 및 관리 — 업계 최고 devlink 통합

mlx5 드라이버는 커널 devlink 프레임워크의 최초이자 가장 완전한 구현체이다. devlink 프레임워크 자체가 mlx5 요구사항에 맞춰 설계되었으며, 이후 다른 드라이버들이 점진적으로 채택하고 있다.

devlink 기능 mlx5 ice bnxt_en ena r8169
Health Reporters 5개 (fw, fw_fatal, tx, rx, vnic) 2개 3개
FW Live Reset ✓ (CX-6+)
Resource Monitoring ✓ (flow tables, counters) 제한적
Port Flavours (SF)
Rate Limiting (per-VF/SF) ✓ (tx_share, tx_max)
Trap (drop 원인 추적)
# 패킷 드롭 원인 추적 (mlx5 전용 trap)
devlink trap show pci/0000:01:00.0
devlink trap set pci/0000:01:00.0 trap source_mac_is_multicast action trap

# SF별 rate limiting (QoS)
devlink port function rate add pci/0000:01:00.0/sf_group \
    tx_share 1gbit tx_max 10gbit

# FW live reset (재부팅 불필요, CX-6+)
devlink dev reload pci/0000:01:00.0 action fw_activate
FW Live Reset의 운영 가치: mlx5의 devlink dev reload action fw_activate는 재부팅 없이 FW를 활성화하여 다운타임을 수 초로 단축한다. 수천 대 서버 환경에서 운영 비용의 근본적 차이를 만든다. 현재 이 기능을 지원하는 NIC 드라이버는 mlx5뿐이다.
종합 비교: 드라이버별 강약점 매트릭스
드라이버 Flow Steering XDP/AF_XDP HW Offload 가상화 RDMA 관측성 종합
mlx5e ★★★★★ ★★★★★ ★★★★★ ★★★★★ ★★★★★ ★★★★★ 30/30
ice ★★★☆☆ ★★★★☆ ★★★☆☆ ★★★☆☆ ★★★☆☆ ★★★☆☆ 19/30
bnxt_en ★★★☆☆ ★★★☆☆ ★★★★☆ ★★★☆☆ ★★☆☆☆ ★★★☆☆ 18/30
cxgb4 ★★☆☆☆ ★☆☆☆☆ ★★★★☆ ★★☆☆☆ ★★☆☆☆ ★★☆☆☆ 13/30
ena ★☆☆☆☆ ★☆☆☆☆ ★☆☆☆☆ ★★★★☆ ★☆☆☆☆ ★★☆☆☆ 10/30
gve ★☆☆☆☆ ★☆☆☆☆ ★☆☆☆☆ ★★★★☆ ★☆☆☆☆ ★★☆☆☆ 10/30
virtio_net ★☆☆☆☆ ★★☆☆☆ ★☆☆☆☆ ★★★★★ ★☆☆☆☆ ★☆☆☆☆ 10/30
r8169 ★☆☆☆☆ ★☆☆☆☆ ★☆☆☆☆ ★☆☆☆☆ ★☆☆☆☆ ★☆☆☆☆ 6/30
평가 기준 참고: ★★★★★ = 업계 최고/참조 구현, ★★★★☆ = 프로덕션 완성도, ★★★☆☆ = 기능 존재하나 범위 제한, ★★☆☆☆ = 기본/레거시 수준, ★☆☆☆☆ = 미지원 또는 최소 구현. 각 드라이버는 설계 목적이 다르므로(ena/gve는 클라우드 최적화, r8169는 소비자용), 낮은 점수가 반드시 품질 문제를 의미하지 않는다. 목적 적합성(fit-for-purpose)을 함께 고려해야 한다.
워크로드별 최적 드라이버 선택 가이드
워크로드 최적 드라이버 mlx5 선택 이유 현실적 대안
AI/HPC 훈련 mlx5 (필수) GPUDirect RDMA, SHARP, IB/RoCE 대안 없음
K8s 대규모 네트워크 정책 mlx5 (권장) 64M+ flow rules, OVS HW offload bnxt (10K 미만)
NFV/VNF 가속 mlx5 SF + eSwitch + CT offload ice (소규모), bnxt
TLS 프록시/LB mlx5 / cxgb4 kTLS inline, CPU 해방 bnxt (kTLS 부분 지원)
XDP 패킷 필터링 mlx5 35 Mpps, AF_XDP ZC ice (25 Mpps)
AWS EC2 ena (필수) - ENA Express/SRD
GCP gve (필수) - DQO 모드
소비자/데스크탑 r8169 과잉 스펙 r8169, atlantic
# mlx5 종합 프로파일링
devlink dev info pci/0000:01:00.0       # HW/FW 정보
ethtool -k enp1s0f0np0 | grep -E "tc-hw|ntuple|rx-gro-hw"
ethtool -S enp1s0f0np0 | head -50       # 성능 카운터
devlink health show pci/0000:01:00.0    # Health 상태
devlink resource show pci/0000:01:00.0  # 리소스 사용률

# bpftrace: mlx5e NAPI poll 효율성
bpftrace -e 'kprobe:mlx5e_napi_poll { @polls = count(); }
kretprobe:mlx5e_napi_poll /retval > 0/ { @pkts = hist(retval); }
interval:s:5 { print(@polls); print(@pkts); clear(@polls); clear(@pkts); }'
mlx5의 약점도 인식해야 한다: (1) 가격 — ConnectX-7 400G NIC는 $1,000+ 수준으로, r8169($5~15) 대비 수십~수백 배. (2) 드라이버 복잡도 — mlx5_core 소스 10만 줄+, 디버깅/커스터마이징 어려움. (3) OFED 의존성 — SHARP, GPUDirect Storage 등 일부 고급 기능은 NVIDIA OFED/DOCA 필요. (4) 전력 소비 — ConnectX-7 TDP 30W+, 저전력 환경 부적합. 워크로드 요구사항과 TCO를 종합적으로 판단해야 한다.

mlx4 레거시 드라이버 및 mlx5 마이그레이션

mlx4_core/mlx4_en은 ConnectX-2, ConnectX-3, ConnectX-3 Pro를 지원하는 레거시 드라이버이다. mlx5와 비교하여 스티어링 유연성, SR-IOV 확장성, offload 기능에서 상당한 차이가 있다. ConnectX-3 Pro는 mlx4와 mlx5 모두에서 부분적으로 지원되지만, mlx4가 권장된다.

mlx4 vs mlx5 아키텍처 비교 mlx4 (Legacy) ConnectX-2 / 3 / 3 Pro mlx4_en Ethernet driver mlx4_ib IB / RoCEv1 mlx4_core HCA 초기화, 포트 관리 제한사항 - 최대 64 VFs (vs mlx5: 1024) - Switchdev 모드 미지원 - TC flower offload 미지원 - AF_XDP zero-copy 미지원 - RoCEv2 미지원 (CX-2/3, v1만) - devlink health 미지원 - TLS/IPsec offload 미지원 - Connection Tracking 미지원 mlx5 (Current) ConnectX-4 ~ 7 / BlueField 1~3 mlx5e Ethernet mlx5_ib RDMA mlx5_vdpa vDPA mlx5_core 통합 코어 + eSwitch + devlink 핵심 기능 - 최대 1024 VFs + Scalable Functions - Switchdev + Representors + FDB - TC flower / OVS HW offload - AF_XDP zero-copy + multi-buf XDP - RoCEv2 + GPUDirect RDMA - devlink health / params / flash - TLS / IPsec / MACsec inline - HW CT + NAT + Sample offload
그림 ndd-26. mlx4 vs mlx5 아키텍처 비교 — 레거시 mlx4의 제한사항과 mlx5의 확장된 기능 세트

mlx4에서 mlx5로의 마이그레이션은 하드웨어 교체(ConnectX-3 → ConnectX-5/6/7)를 수반한다. 소프트웨어 관점에서 주요 변경 사항과 마이그레이션 절차는 다음과 같다.

항목 mlx4 mlx5 마이그레이션 시 주의
커널 모듈 mlx4_core, mlx4_en, mlx4_ib mlx5_core (통합) modprobe 설정 변경
인터페이스 명명 eth0, eth1 (legacy) enp1s0f0np0 (predictable) 네트워크 설정 파일 수정
SR-IOV 설정 mlx4_core num_vfs 모듈 파라미터 sysfs sriov_numvfs 부팅 스크립트 변경
eSwitch 모드 Legacy만 지원 Legacy + Switchdev OVS 환경은 Switchdev 권장
QoS 설정 mlnx_qos (OFED) mlnx_qos + tc + devlink TC-based QoS로 전환 권장
FW 관리 mlxfwmanager (OFED) devlink dev flash (in-tree) OFED 도구 의존성 제거 가능
RDMA IB / RoCEv1 IB / RoCEv1 / RoCEv2 RoCEv2 전환 시 ECN/PFC 설정
XDP Native XDP (기본) Native + AF_XDP + multi-buf AF_XDP 앱 활용 가능
# mlx4 → mlx5 마이그레이션 전 현재 설정 백업
ethtool -i eth0                        # 드라이버 확인 (mlx4_en)
mlxconfig -d /dev/mst/mt4099_pciconf0 query > mlx4_config_backup.txt
ip addr show eth0 > mlx4_ip_backup.txt

# 하드웨어 교체 후 mlx5 드라이버 확인
lspci -d 15b3: -k                      # mlx5_core가 로드되어야 함
ethtool -i enp1s0f0np0                 # driver: mlx5_core

# SR-IOV 마이그레이션 (mlx4 → mlx5 방식)
# 기존 mlx4: modprobe mlx4_core num_vfs=4,0 port_type_array=2,2
# 새 mlx5:
echo 4 > /sys/class/net/enp1s0f0np0/device/sriov_numvfs

# Switchdev 모드 활용 (mlx4에서 불가했던 기능)
devlink dev eswitch set pci/0000:01:00.0 mode switchdev

# 기존 mlx4 NIC 관련 모듈 정리
echo "blacklist mlx4_core" >> /etc/modprobe.d/blacklist-mlx4.conf
echo "blacklist mlx4_en" >> /etc/modprobe.d/blacklist-mlx4.conf

mlx4 지원 상태: mlx4 드라이버는 커널에서 유지보수 모드(maintenance mode)로 관리되며, 새로운 기능은 추가되지 않는다. 보안 패치와 심각한 버그 수정만 적용된다. ConnectX-3 Pro 이하 하드웨어를 사용 중이라면, 가능한 빨리 ConnectX-5 이상으로 업그레이드하여 mlx5 드라이버의 활발한 개발 혜택을 받는 것이 권장된다. 특히 커널 6.x 이후로 mlx5에 추가된 기능(SF, CT offload, inline crypto 등)은 mlx4에서 사용할 수 없다.

ConnectX-3 CI vs CQ 모델

mlx4는 완료 처리를 위해 두 가지 모델을 제공한다. CI(Completion Index) 모드는 드라이버가 CQ를 폴링하지 않고 완료 인덱스를 직접 쿼리하여 처리하는 방식으로, TX 경로의 저지연에 유리하다. 반면 CQ(Completion Queue) 폴링 모드는 NAPI 루프 안에서 CQE 배치를 한꺼번에 처리하므로 높은 처리량(throughput) 환경에 적합하다. CI 모드는 코어당 처리 패킷이 적을 때 캐시 압력을 줄이고, CQ 폴링 모드는 패킷이 몰릴 때 배치 효율로 CPU 사이클을 절약한다.

ConnectX-3는 IB 포트와 Ethernet 포트를 동일 HCA 위에서 멀티플렉싱한다. mlx4_core는 부팅 시 포트 타입(IB vs Ethernet)을 port_type_array 모듈 파라미터로 설정하며, 각 포트는 독립적인 CQ/QP 자원 풀을 갖는다. 두 포트가 공유하는 HCA 리소스(EQ, MPT 등)는 mlx4_core가 중재한다.

QP 리소스 한계: mlx4는 포트당 최대 QP 및 CQ 수가 하드웨어에 고정되어 있으며, SR-IOV 환경에서는 VF 수 제한이 64로 고정된다(SRIOV_MAX_FUNCS = 64). 이 제한은 mlx4_core 내부 리소스 파티셔닝 테이블이 64-entry로 설계된 데서 기인한다. ConnectX-4 이상(mlx5)에서는 이 한계가 1024 VF으로 확장되었다.

mlx4 운영 가이드

ConnectX-3 환경에서 중요한 모니터링 지표와 커널 호환성은 다음과 같다.

카운터/지표확인 방법임계값 기준
TX/RX 패킷 드롭ethtool -S eth0 | grep drop0이 정상; 증가 시 링 버퍼 확대 검토
PCI 오류dmesg | grep mlx4AER 오류 발생 시 슬롯 교체 고려
FW Healthmlx4 장치 로그 조회syndrome 코드 0x0이 정상
QP/CQ 고갈rdma stat showfree QP 0에 근접 시 VF 축소 필요
온도 / 전력mlxconfig -d /dev/mst/... query규격 최대치 90% 이하 유지

커널 호환성: mlx4 드라이버는 커널 4.x ~ 6.x에서 모두 컴파일되지만, 커널 5.15 이후로는 신규 기능 추가 없이 보안 패치와 버그 수정만 이루어지는 유지보수 모드(maintenance mode)다. 커널 6.8부터는 일부 레거시 compat 코드가 제거되었으므로, ConnectX-3 Pro 사용 환경에서는 커널 버전 업그레이드 전 OFED 호환성을 반드시 확인해야 한다. 권장 마이그레이션 시점: 커널 6.x 이상 + ConnectX-3 조합은 ConnectX-5/6으로의 하드웨어 업그레이드가 강력히 권장된다.

BlueField DPU와 mlx5

NVIDIA BlueField DPU(Data Processing Unit)는 mlx5 NIC에 ARM 코어 클러스터를 내장하여 네트워크 처리를 호스트 CPU에서 완전히 분리(offload)하는 아키텍처다.

모델ARM 코어NIC 속도주요 오프로드호스트 인터페이스
BF1 (BlueField-1)Cortex-A72 × 1625GbE × 2OVS 기본, IPsecPCIe 3.0 × 8
BF2 (BlueField-2)Cortex-A72 × 8100GbE × 2OVS, CT, crypto, DOCAPCIe 4.0 × 16
BF3 (BlueField-3)Cortex-A78AE × 16400GbE × 2OVS, AI inference, StoragePCIe 5.0 × 16

호스트에서 BlueField는 두 가지 netdev로 나타난다: 호스트 representor(호스트 측 트래픽을 DPU ARM에서 제어)와 물리 포트 representor(외부 네트워크 포트). DPU ARM 위에서 OVS, eBPF, connection tracking이 실행되며, 호스트 CPU는 패킷 처리 부담에서 해방된다.

ConnectX-3(mlx4) → ConnectX-4+(mlx5) 마이그레이션 시 주요 아키텍처 차이: mlx5는 단일 mlx5_core 모듈이 Ethernet, RDMA, vDPA를 모두 담당하는 통합 구조이며, eSwitch, Scalable Function(SF), devlink health 등이 mlx4에는 없는 핵심 기능이다. DPU 오프로드 기능(DOCA SDK)은 mlx5 기반 BlueField에서만 사용 가능하다.

mlx4 지원 상태: mlx4 드라이버는 커널에서 유지보수 모드(maintenance mode)로 관리되며, 새로운 기능은 추가되지 않는다. 보안 패치와 심각한 버그 수정만 적용된다. ConnectX-3 Pro 이하 하드웨어를 사용 중이라면, 가능한 빨리 ConnectX-5 이상으로 업그레이드하여 mlx5 드라이버의 활발한 개발 혜택을 받는 것이 권장된다. 특히 커널 6.x 이후로 mlx5에 추가된 기능(SF, CT offload, inline crypto 등)은 mlx4에서 사용할 수 없다.

Broadcom: bnxt_en / bnx2x / tg3

Broadcom은 서버 및 데이터센터 시장에서 가장 광범위한 NIC 포트폴리오를 보유하고 있습니다. 최신 Thor2(BCM57608) 시리즈부터 레거시 Tigon3까지, 세 가지 주요 드라이버가 리눅스 커널에서 관리됩니다.

드라이버 칩셋 시리즈 최대 속도 NAPI XDP SR-IOV TruFlow 소스 경로
bnxt_en BCM573xx / BCM574xx / BCM57608 (Thor2) 400 GbE O O O (256 VFs) O drivers/net/ethernet/broadcom/bnxt/
bnx2x BCM57710 / BCM57711 / BCM57810 10 GbE O X O (64 VFs) X drivers/net/ethernet/broadcom/bnx2x/
tg3 BCM5700 / BCM5719 / BCM5720 1 GbE O X X X drivers/net/ethernet/broadcom/tg3.*

bnxt_en 아키텍처: HWRM과 링 모델

bnxt_en 드라이버의 핵심 설계 원칙은 HWRM(Hardware Resource Manager) 펌웨어 인터페이스입니다. 호스트 드라이버는 하드웨어 레지스터를 직접 조작하지 않고, HWRM 명령 채널을 통해 모든 설정과 리소스 관리를 수행합니다. 이 구조는 펌웨어 업데이트만으로 새로운 기능을 추가할 수 있는 유연성을 제공합니다.

HWRM 통신 메커니즘: 호스트는 공유 메모리 영역에 HWRM 요청 메시지를 작성하고 도어벨 레지스터를 울립니다. 펌웨어가 요청을 처리한 후 응답을 같은 영역에 기록하고 인터럽트로 완료를 알립니다. 기본 타임아웃은 500ms이며, HWRM_FUNC_QCFG 같은 복잡한 명령은 최대 5초까지 대기합니다.

Host Driver (bnxt_en) HWRM Command Channel TX Rings RX Rings CP Rings AGG Rings Doorbell Registers (BAR) NAPI Poll XDP Prog ethtool / devlink Interface HWRM Firmware Command Processor Ring Mgr Port Mgr VF Mgr Stat Mgr FW Health Monitor Thor2 ASIC Packet Engine (L2/L3/L4) TruFlow RSS Engine DMA Engine (Host ↔ NIC) MAC 0 MAC 1 SerDes (PAM4 / NRZ) Integrated PHY 100G/200G/400G HWRM DMA (Ring Data) Doorbell
그림 ndd-27. bnxt_en 전체 아키텍처 — Host Driver가 HWRM을 통해 Thor2 ASIC의 모든 리소스를 관리

링(Ring) 모델은 bnxt_en의 데이터 경로 핵심입니다. 4가지 링 타입이 서로 연동하여 패킷 송수신을 처리합니다.

TX Ring tx_buf_ring[] (SW) tx_desc_ring[] (HW BD) Producer: 드라이버 Consumer: HW BD 타입: SHORT/LONG/INLINE 기본: 512 엔트리 TSO: 다중 BD 체인 Doorbell로 HW 알림 STOP 임계: 엔트리 < MAX_SKB_FRAGS RX Ring rx_buf_ring[] (SW) rx_desc_ring[] (HW BD) Producer: 드라이버 Consumer: HW 버퍼 크기: PAGE_SIZE (헤더+데이터) 기본: 512 엔트리 점보: AGG 링 연계 HW GRO 지원 XDP: redirect/pass/drop Completion Ring cp_desc_ring[] (HW CMP) Producer: HW Consumer: NAPI CMP 타입: TX_L2 (TX 완료) RX_L2 (RX 완료) 기본: 1024 엔트리 Toggle bit으로 유효성 MSI-X 벡터 1:1 매핑 HWRM Async Event도 이 링으로 전달 AGG Ring rx_agg_ring[] (SW) rx_agg_desc_ring[] (BD) Producer: 드라이버 Consumer: HW 점보 프레임 전용: RX BD → AGG BD 체인 기본: 1024 엔트리 PAGE_SIZE 단위 버퍼 Scatter-Gather 수신 MTU > PAGE_SIZE 시 활성
그림 ndd-28. bnxt_en 4종 링 구조 — TX/RX/Completion/AGG 링의 역할과 Producer/Consumer 관계
HWRM 명령 카테고리 주요 명령 용도
Function FUNC_QCFG, FUNC_CFG, FUNC_RESET PF/VF 설정 조회 및 변경, 펑션 리셋
Ring RING_ALLOC, RING_FREE, RING_GRP_ALLOC TX/RX/CP/AGG 링 할당 및 그룹화
VNIC VNIC_ALLOC, VNIC_CFG, VNIC_RSS_CFG 가상 NIC 설정, RSS 해시 키 및 인디렉션 테이블
Port PORT_PHY_QCFG, PORT_PHY_CFG PHY 상태 조회, 속도/FEC/AN 설정
Stat STAT_CTX_ALLOC, STAT_CTX_QUERY 통계 컨텍스트 할당 및 카운터 조회
CFA CFA_FLOW_ALLOC, CFA_FLOW_FREE TruFlow 하드웨어 오프로드 플로우 규칙
Queue QUEUE_QPORTCFG, QUEUE_CFG QoS 큐 설정, CoS 매핑

링 크기 튜닝: 고처리량 워크로드에서는 ethtool -G eth0 rx 4096 tx 4096로 링 크기를 늘립니다. Completion 링은 자동으로 rx + tx의 2배로 조정됩니다. AGG 링은 MTU가 PAGE_SIZE를 초과할 때만 활성화되며, rx_jumbo 파라미터로 크기를 별도 조절합니다.

# bnxt_en 링 및 채널 설정 확인
ethtool -g enp3s0f0
ethtool -l enp3s0f0

# 링 크기 최적화 (고처리량 서버)
ethtool -G enp3s0f0 rx 4096 tx 4096

# 결합 채널 수 조정 (CPU 수에 맞춤)
ethtool -L enp3s0f0 combined 16

# HWRM 버전 및 펌웨어 정보 확인
ethtool -i enp3s0f0
# firmware-version: 228.1.105.2 (HWRM 1.10.2.128)

# 인터럽트 코얼레싱 (적응형)
ethtool -C enp3s0f0 adaptive-rx on adaptive-tx on

# 하드웨어 GRO 활성화
ethtool -K enp3s0f0 rx-gro-hw on
/* bnxt_en HWRM 명령 전송 핵심 구조 */
struct hwrm_req {
    __le16 req_type;          /* 명령 타입 */
    __le16 cmpl_ring;         /* 완료 링 ID */
    __le16 seq_id;            /* 시퀀스 번호 */
    __le16 target_id;         /* 대상 (PF/VF) */
    __le64 resp_addr;         /* 응답 DMA 주소 */
};

/* 링 그룹: TX + RX + CP + AGG를 하나로 묶음 */
struct bnxt_ring_grp_info {
    u16 fw_stats_ctx;         /* 통계 컨텍스트 FW ID */
    u16 fw_grp_id;            /* FW 그룹 ID */
    u16 rx_fw_ring_id;
    u16 agg_fw_ring_id;
    u16 cp_fw_ring_id;
};
HWRM Timeout 감지 및 복구

bnxt_hwrm_do_send_msg()는 HWRM 명령을 펌웨어로 전송하고 완료를 기다리는 핵심 함수입니다. 기본 타임아웃은 500ms이며, HWRM_FUNC_QCFG나 링 초기화처럼 복잡한 명령은 최대 10초까지 대기합니다. 타임아웃이 발생하면 드라이버는 펌웨어 사망(FW Fatal)으로 간주하고 복구 절차를 시작합니다.

HWRM Timeout과 라이브 마이그레이션: VM 라이브 마이그레이션(vMotion/live-migrate) 도중 하이퍼바이저가 vCPU를 일시 정지하면, VF의 HWRM polling 루프도 멈춥니다. 마이그레이션 완료 후 재개 시점에 이미 타임아웃이 경과하여 VF 드라이버가 FW 오류를 보고할 수 있습니다. 완화책: HWRM_FUNC_CFGasync_event_completion_timeout을 충분히 크게 설정하거나, 하이퍼바이저 측 VFIO 드라이버가 마이그레이션 전에 VF 큐를 정지하도록 구성합니다.

PF 충돌 → VF 연쇄 영향: PF 드라이버의 HWRM 채널이 실패하면, 모든 VF는 HWRM 프록시를 잃습니다. VF는 다음 HWRM 요청에서 타임아웃을 만나고, 이를 FLR(Function Level Reset) 트리거로 처리합니다. FLR 이후 VF 드라이버는 HWRM_FUNC_VF_CFG를 다시 발행하여 채널을 재확립합니다.

PF HWRM 채널 실패
        │
        ├─ PF: bnxt_fw_reset() 호출
        │       └─ bnxt_fw_fatal_reporter 활성화
        │
        └─ VF들: 다음 HWRM 요청 → 타임아웃(500ms)
                └─ bnxt_vf_hwrm_timeout_notify()
                        ├─ netif_carrier_off()
                        ├─ FLR 요청 (PCIe FLR)
                        └─ 채널 재확립 (HWRM_FUNC_VF_CFG)
HWRM 명령 트레이싱

HWRM 명령 흐름을 추적하는 방법은 devlink health reporter, ftrace, bpftrace 세 가지가 있습니다. devlink reporter는 FW 이상 상태를 진단하고, ftrace/bpftrace는 정상 동작 중 지연을 측정하는 데 사용됩니다.

# devlink health reporter: FW 상태 확인
devlink health show pci/0000:03:00.0
# reporter: bnxt_fw_reporter — FW 링크 이벤트 추적
# reporter: bnxt_fw_fatal_reporter — FW 크래시 덤프
devlink health diagnose pci/0000:03:00.0 reporter bnxt_fw_reporter
devlink health dump show pci/0000:03:00.0 reporter bnxt_fw_fatal_reporter

# ftrace: HWRM send/receive 함수 추적
echo 'bnxt_hwrm_do_send_msg' > /sys/kernel/debug/tracing/set_ftrace_filter
echo function > /sys/kernel/debug/tracing/current_tracer
echo 1 > /sys/kernel/debug/tracing/tracing_on
# 작업 수행 후
cat /sys/kernel/debug/tracing/trace | grep bnxt_hwrm | head -20
echo 0 > /sys/kernel/debug/tracing/tracing_on
echo nop > /sys/kernel/debug/tracing/current_tracer

# bpftrace: HWRM 명령 레이턴시 히스토그램
bpftrace -e '
kprobe:bnxt_hwrm_do_send_msg { @start[tid] = nsecs; }
kretprobe:bnxt_hwrm_do_send_msg
/@start[tid]/
{
    @latency_us = hist((nsecs - @start[tid]) / 1000);
    delete(@start[tid]);
}
END { print(@latency_us); }'

# HWRM 타임아웃 카운터 확인 (ethtool 통계)
ethtool -S enp3s0f0 | grep -i hwrm
# hwrm_req_timeout: 타임아웃 발생 횟수
# hwrm_resp_err: 응답 오류 횟수

# 현재 HWRM 타임아웃 설정 확인
ethtool -i enp3s0f0
# 모듈 파라미터로 타임아웃 조정 (ms 단위)
modprobe bnxt_en hwrm_min_timeout=1000

bnxt_en TruFlow 오프로드 엔진

TruFlow는 Broadcom Thor/Thor2 NIC에 내장된 프로그래머블 패킷 처리 파이프라인입니다. TC flower 규칙을 하드웨어로 직접 오프로드하여 호스트 CPU 부하를 제거하고, 수백만 개의 플로우 규칙을 와이어 스피드로 처리합니다.

Ingress Packet Parser L2/L3/L4 헤더 터널 디캡슐 Key 추출 TCAM 와일드카드 매칭 우선순위 기반 Exact Match 해시 기반 룩업 수백만 엔트리 CFA (Context Filter Accelerator) Action Forward Drop Modify (NAT) Count Forward → VF/Port Drop Modify NAT/VLAN/TTL Miss → Host Flow Stats pkt/byte counters
그림 ndd-29. TruFlow 오프로드 파이프라인 — Parser → TCAM/EM 룩업 → Action 단계를 거쳐 패킷 처리
# TC flower를 이용한 TruFlow 오프로드 예시

# switchdev 모드 활성화 (TruFlow 필수 전제)
devlink dev eswitch set pci/0000:03:00.0 mode switchdev

# ingress qdisc 추가
tc qdisc add dev enp3s0f0 ingress

# VXLAN 터널 트래픽을 VF representor로 포워드
tc filter add dev enp3s0f0 protocol ip parent ffff: \
  flower enc_dst_ip 10.0.0.1 enc_key_id 100 enc_dst_port 4789 \
  action tunnel_key unset \
  action mirred egress redirect dev enp3s0f0_0

# NAT 오프로드 (SNAT)
tc filter add dev enp3s0f0 protocol ip parent ffff: \
  flower ip_proto tcp src_ip 192.168.1.0/24 \
  action pedit ex munge ip src set 10.0.0.1 \
  action csum ip4h tcp \
  action mirred egress redirect dev enp3s0f0

# 오프로드된 플로우 통계 확인
tc -s filter show dev enp3s0f0 ingress

# TruFlow 테이블 사용량 확인
devlink resource show pci/0000:03:00.0

TruFlow 용량: Thor2 칩셋은 EM(Exact Match) 테이블에 최대 400만 엔트리, TCAM에 최대 16K 와일드카드 규칙을 지원합니다. OVS-DPDK 환경에서는 CT(Connection Tracking) 오프로드를 통해 커넥션당 양방향 플로우를 자동 설치합니다.

TruFlow TCAM 프래그먼테이션

TCAM은 와일드카드 규칙을 우선순위 순서로 저장합니다. 규칙 추가/삭제가 반복되면 우선순위 홀(priority hole)이 발생하여 실제 사용 가능한 용량이 표시 용량보다 줄어드는 프래그먼테이션 현상이 일어납니다.

프래그먼테이션 발생 메커니즘: 우선순위 1000의 규칙을 삽입하면 기존 규칙들이 물리적으로 재배치됩니다. 규칙 삭제 시 해당 슬롯은 빈 상태로 남고, 새 규칙 삽입 시 우선순위 위치가 맞지 않으면 그 슬롯을 재사용하지 못합니다. 이 과정이 반복되면 TCAM의 논리 용량이 줄어듭니다.

Defragmentation: 펌웨어는 TCAM 사용률이 임계값(기본 80%)을 초과하면 자동으로 defrag를 시작합니다. Defrag 중에는 새 규칙 설치가 최대 수십 ms 지연될 수 있으므로, 고속 플로우 설치 환경에서는 주의가 필요합니다.

# TCAM/EM 리소스 사용량 모니터링
devlink resource show pci/0000:03:00.0
# 예시 출력:
#   resource TCAM: size 16384 unit entry occ 12800 -> 78%
#   resource EM:   size 4194304 unit entry occ 980000 -> 23%

# TC flower 현재 오프로드 규칙 수 확인
tc filter show dev enp3s0f0 ingress | grep -c 'handle'

# 오프로드 성공/실패 구분 (in_hw 플래그)
tc filter show dev enp3s0f0 ingress | grep -A3 'flower'
# in_hw: HW 오프로드 성공 / not_in_hw: 소프트웨어 폴백
TruFlow 용량 계획 가이드

EM 해시 충돌: Exact Match 테이블은 해시 기반이므로 생일 역설(birthday paradox)에 따른 충돌이 발생합니다. 테이블 크기가 N이고 k개의 엔트리가 채워졌을 때, 예상 충돌 확률은 약 k²/(2N)입니다. Thor2의 4M EM 테이블에서 100만 엔트리 사용 시 예상 충돌률은 약 12%입니다. 충돌 시 엔트리는 소프트웨어로 폴백됩니다.

TCAM vs EM 결정 기준:

CT 엔트리 추정 공식:

필요 EM 엔트리 = 동시 연결 수 × 2 (양방향) × 1.1 (10% 헤드룸)

예시: 동시 연결 50만 개
  = 500,000 × 2 × 1.1 = 1,100,000 엔트리 필요
  Thor2 4M EM: 충분 (사용률 26%)
  Thor 1M EM:  부족 → TCAM 보조 또는 소프트웨어 폴백
칩셋 SKU EM 용량 TCAM 용량 CT 최대 연결 (권장)
Thor (BCM57508) 100G × 2 1M 엔트리 8K 와일드카드 ~400K 동시 연결
Thor (BCM57504) 25G × 4 1M 엔트리 8K 와일드카드 ~400K 동시 연결
Thor2 (BCM57608) 100G × 2 4M 엔트리 16K 와일드카드 ~1.6M 동시 연결
Thor2 (BCM57604) 25G × 4 4M 엔트리 16K 와일드카드 ~1.6M 동시 연결
# 현재 EM 사용량 및 헤드룸 계산
EM_MAX=$(devlink resource show pci/0000:03:00.0 | awk '/EM/{print $4}')
EM_OCC=$(devlink resource show pci/0000:03:00.0 | awk '/EM/{print $6}')
echo "EM 사용률: $((EM_OCC * 100 / EM_MAX))%"
echo "남은 헤드룸: $((EM_MAX - EM_OCC)) 엔트리"

# CT 오프로드 통계 (conntrack + TruFlow)
conntrack -S | grep -E 'found|insert_failed|drop'
# insert_failed가 증가하면 EM 용량 부족 신호

# TruFlow 오프로드 상태 요약
devlink resource show pci/0000:03:00.0 | grep -E 'TCAM|EM'
TruFlow TCAM/EM 용량 계획 결정 트리 새 플로우 규칙 추가 와일드카드/마스크 포함? Yes TCAM 와일드카드 룩업 TCAM 여유 있음? Yes TCAM 설치 우선순위 배치 No SW 폴백 CPU 처리 (성능↓) → TCAM 증설 검토 No (정확 매칭) Exact Match (EM) 해시 룩업 해시 충돌 발생? No EM 설치 성공 와이어스피드 처리 Yes TCAM 계층 룩업 충돌 엔트리 TCAM 이동 → TCAM 용량 소비 혼합 시나리오 TCAM → EM 2단계 계층 룩업
그림 ndd-70. TruFlow TCAM/EM 용량 계획 결정 트리 — 규칙 특성에 따라 TCAM(와일드카드), EM(정확 매칭), 또는 계층 룩업 경로 선택

bnxt_en의 SR-IOV 구현은 HWRM을 통한 완전한 VF 리소스 분할을 특징으로 합니다. PF 드라이버가 HWRM 프록시 역할을 하여 VF의 모든 하드웨어 요청을 중개합니다.

PF Driver HWRM Proxy (VF 대행) devlink eswitch VF 최대 256개 VF 0 Ring 0-3 VNIC 0 Stat Ctx 0 VF 1 Ring 4-7 VNIC 1 Stat Ctx 1 ... VF n Ring n*4.. VNIC n Stat Ctx n HWRM FW Resource Manager Ring Pool VNIC Pool Stat Pool MSI-X Pool 접근 제어 (VF 격리) NIC HW TX/RX Queues RSS/TruFlow MAC/VLAN Filter SerDes/PHY HWRM Cmd VF→PF Proxy VF Representors (switchdev 모드) enp3s0f0_0, enp3s0f0_1, ... enp3s0f0_n → OVS/TC 연동
그림 ndd-30. bnxt_en PF-VF HWRM 모델 — PF가 VF의 HWRM 요청을 프록시하여 리소스 격리 보장
# SR-IOV VF 생성
echo 8 > /sys/class/net/enp3s0f0/device/sriov_numvfs

# VF MAC 주소 설정 (보안 강화)
ip link set enp3s0f0 vf 0 mac 00:11:22:33:44:55
ip link set enp3s0f0 vf 0 vlan 100
ip link set enp3s0f0 vf 0 spoofchk on
ip link set enp3s0f0 vf 0 trust on         # 필요시 promiscuous 허용

# VF 대역폭 제한 (Mbps)
ip link set enp3s0f0 vf 0 max_tx_rate 1000
ip link set enp3s0f0 vf 0 min_tx_rate 100

# devlink 정보 확인
devlink dev info pci/0000:03:00.0
devlink port show pci/0000:03:00.0

# switchdev 모드 전환 (VF representor 생성)
devlink dev eswitch set pci/0000:03:00.0 mode switchdev

# devlink health reporter
devlink health show pci/0000:03:00.0
devlink health diagnose pci/0000:03:00.0 reporter fw

# 펌웨어 업데이트 (devlink flash)
devlink dev flash pci/0000:03:00.0 file bnxt_fw.pkg

bnx2x 레거시: 57810 NPAR 아키텍처

bnx2x는 BCM57810 시리즈의 10GbE 드라이버로, NPAR(NIC Partitioning) 기능이 핵심 차별점입니다. NPAR은 SR-IOV와 달리 하드웨어 수준에서 물리 포트를 완전히 독립된 가상 NIC으로 분할합니다.

Physical Port 10 GbE MCP Management Controller BW Arbiter NPAR 분배 관리 NPAR 0 MAC: 00:11:22:33:44:00 Queue 0-3 CP 0-3 BW: min 25% / max 40% iSCSI Offload NPAR 1 MAC: 00:11:22:33:44:01 Queue 4-7 CP 4-7 BW: min 25% / max 50% FCoE Offload NPAR 2 MAC: 00:11:22:33:44:02 Queue 8-11 CP 8-11 BW: min 25% / max 30% 네트워크 전용 NPAR 3 MAC: 00:11:22:33:44:03 Queue 12-15 CP 12-15 BW: min 25% / max 30% 관리용(IPMI) Host OS 1 eth0 (NPAR 0) Host OS 2 eth0 (NPAR 1) VMware ESXi vmnic2 (NPAR 2)
그림 ndd-31. bnx2x NPAR 구조 — 물리 포트를 4개의 독립 가상 NIC으로 분할, 각각 고유 MAC/큐/대역폭 보유
비교 항목 NPAR SR-IOV
MAC 주소 펌웨어에서 영구 할당 (NVM) PF 드라이버가 동적 할당
OS에서 보이는 형태 독립 PCI Function (PF0~PF3) VF (PF에 종속)
대역폭 제어 하드웨어 MCP가 보장 (min/max) 소프트웨어 기반
프로토콜 오프로드 파티션별 iSCSI/FCoE 전용 할당 가능 PF만 오프로드 지원
장애 격리 완전 격리 (PF 리셋 독립) PF 리셋 시 VF도 영향
구성 변경 NVM 수정 후 재부팅 필요 런타임 동적 변경 가능

bnx2x 알려진 이슈: 커널 6.x에서 bnx2x의 MDC/MDIO 타이밍 관련 PHY 초기화 실패가 간헐적으로 발생합니다. bnx2x.debug=0x20 모듈 파라미터로 PHY 디버그 로그를 활성화할 수 있습니다. 또한 IOMMU가 활성화된 환경에서 DMA 매핑 오류 시 iommu=pt 부트 옵션이 필요할 수 있습니다.

# bnx2x NPAR 설정 확인 (펌웨어 유틸리티 사용)
bnxtnvm -dev=eth0 listcfg

# 현재 대역폭 할당 확인
ethtool -i eth0 | grep firmware

# bnx2x 디버그 레벨 설정
modprobe bnx2x debug=0x20

# 실행 중 디버그 변경
echo 0x20 > /sys/module/bnx2x/parameters/debug

# bnx2x 드라이버 통계
ethtool -S eth0 | grep -E 'rx_|tx_|brb_|pfc_'
bnx2x NPAR 운영 심화

NPAR은 단순한 포트 분할을 넘어 세밀한 대역폭 보장과 프로토콜 오프로드 분리를 제공합니다. 실제 운영 환경에서는 파티션별 최소/최대 대역폭 설정과 SR-IOV 조합이 핵심 사용 패턴입니다.

대역폭 할당 예시 (4 파티션, 10GbE 포트):

파티션 용도 최소 BW 최대 BW 보장 대역폭
NPAR 0 (eth0) 일반 데이터 트래픽 25% (2.5G) 100% (10G) 항상 2.5G 이상 보장
NPAR 1 (eth1) iSCSI 스토리지 25% (2.5G) 50% (5G) 스토리지 트래픽 격리
NPAR 2 (eth2) FCoE 25% (2.5G) 50% (5G) FC 트래픽 전용
NPAR 3 (eth3) 관리 (IPMI) 25% (2.5G) 10% (1G) 관리 트래픽 분리

NPAR + SR-IOV 조합: NPAR 파티션 위에 SR-IOV VF를 할당할 수 있습니다. 각 파티션은 독립된 PCI Function이므로 VF는 해당 파티션의 대역폭 한도 내에서만 동작합니다. 예를 들어 NPAR 0(최대 40%) 위에 4개 VF를 생성하면, VF들은 합산 40% 한도 내에서 경쟁합니다.

MCP 펌웨어 업데이트 위험: bnx2x의 MCP(Management Controller Processor) 펌웨어 업데이트는 오류 발생 시 NIC이 완전히 응답 불능 상태(bricking)가 될 수 있습니다. 안전한 업데이트 절차는 다음과 같습니다.

  1. 모든 트래픽 중지: ip link set eth0 down (모든 NPAR 파티션)
  2. 현재 펌웨어 백업: bnxtnvm -dev=eth0 backup
  3. 이중화 경로 확인: 다른 NIC으로 관리 접속 유지
  4. 업데이트 실행: bnxtnvm -dev=eth0 update -f new_fw.pkg
  5. 업데이트 중 절전 모드/재부팅 금지 (전원 차단 = 브릭킹)
# NPAR 대역폭 설정 확인 (펌웨어 NVM에서 읽음)
bnxtnvm -dev=eth0 listcfg | grep -i bandwidth

# NPAR 파티션 별 인터페이스 목록
ip link show | grep -E 'eth[0-3]'

# 각 파티션 통계 (MCP BW 중재 결과 확인)
ethtool -S eth0 | grep -E 'npar|partition|bw'

# SR-IOV VF 생성 (NPAR 0 파티션 위에)
echo 4 > /sys/class/net/eth0/device/sriov_numvfs
# VF들은 NPAR 0의 대역폭 한도 내에서 동작

# NPAR + SR-IOV 상태 확인
ip link show eth0

# MCP 펌웨어 버전 확인
ethtool -i eth0 | grep -E 'firmware|version'
# firmware-version: BC1.5.1.0 NCSI v1.3.22.0

tg3 아키텍처: BMC 사이드밴드 통합

tg3 드라이버는 BCM5719/BCM5720 시리즈를 관리하며, 서버 관리 포트로 널리 사용됩니다. 이 NIC의 가장 큰 특징은 BMC(Baseboard Management Controller)와의 사이드밴드 채널을 통한 원격 관리 통합입니다.

Host CPU / OS tg3 드라이버 eth0 (data) eth1 (mgmt) PCIe Bus (Gen2 x1) APE FW 인터페이스 BCM5719/5720 NIC APE (ARM Processor Engine) NCSI / ASF / IPMI 처리 TX DMA RX DMA MAC (L2 Filter / VLAN) Copper PHY SerDes NC-SI Sideband Channel BMC IPMI Stack SOL KVM/vMedia Redfish / Web UI SMBus / I2C 연결 네트워크 (RJ45) PCIe NC-SI Sideband ASF 2.0 경로 Heartbeat (ARP/UDP) Alert (PET Trap → SNMP)
그림 ndd-32. tg3 + BMC 사이드밴드 통합 — APE 프로세서가 NC-SI를 통해 BMC와 NIC 포트를 공유

BMC 공유 NIC 주의사항: BMC가 NC-SI로 NIC 포트를 공유할 때, tg3 드라이버는 APE 펌웨어와 동기화하여 MAC 필터를 관리해야 합니다. tg3_ape_lock() / tg3_ape_unlock() 함수가 이 동기를 담당합니다. BMC 트래픽은 호스트 OS의 네트워크 통계에 포함되지 않으므로, ethtool -S의 카운터만으로는 전체 포트 트래픽을 파악할 수 없습니다.

# tg3 드라이버 정보 확인
ethtool -i eth0
# driver: tg3, firmware-version: 5719-v1.46 NCSI v1.5.18.0

# APE 펌웨어 상태 (dmesg)
dmesg | grep -i "tg3.*ape"

# BMC 공유 모드 확인
ipmitool lan print 1

# tg3 WoL(Wake on LAN) 설정
ethtool -s eth0 wol g

# PHY 속도 고정 (Auto-negotiation 비활성화 시)
ethtool -s eth0 speed 1000 duplex full autoneg off

# tg3 코얼레싱 최적화 (저지연)
ethtool -C eth0 rx-usecs 10 tx-usecs 10 rx-frames 4

# 흔한 문제: EEE(Energy Efficient Ethernet) 비호환 스위치
ethtool --set-eee eth0 eee off

BCM5719 EEE 이슈: 일부 스위치와의 EEE 호환성 문제로 링크가 간헐적으로 드롭될 수 있습니다. 서버 환경에서는 ethtool --set-eee eth0 eee off로 EEE를 비활성화하는 것이 권장됩니다. 또한 tg3의 TSO 관련 체크섬 오류가 특정 펌웨어 버전에서 보고되므로, 문제 발생 시 ethtool -K eth0 tso off를 시도합니다.

tg3 TX/RX Descriptor 링 포맷

tg3의 TX 경로는 4개의 TX Producer 링이 단일 Consumer 모델로 수렴하는 구조입니다. 각 Producer 링은 우선순위가 다른 트래픽(일반/고우선순위/iSCSI/콘솔)에 할당되며, MAC은 Consumer 측에서 이를 스케줄링합니다.

TX Descriptor 포맷: 각 TX BD(Buffer Descriptor)는 64비트로 구성되며 start/mid/end 플래그로 멀티-BD 패킷(TSO, 점보 프레임)을 표현합니다. vlan_tag 필드는 VLAN 태그를 하드웨어가 직접 삽입하도록 지시하며, TSO 관련 mss/hdr_len도 BD에 인라인으로 포함됩니다.

/* tg3 TX Buffer Descriptor (64비트, drivers/net/ethernet/broadcom/tg3.h) */
struct tg3_tx_buffer_desc {
    u32 addr_hi;          /* 상위 32비트 DMA 주소 */
    u32 addr_lo;          /* 하위 32비트 DMA 주소 */
    u32 len_flags;        /* [31:16]=길이, [15:0]=플래그 */
    u32 vlan_tag;         /* VLAN 태그 (HW 삽입) */
};

/* len_flags 플래그 주요 비트 */
/* TXD_FLAG_START (0x0002): 패킷 첫 번째 BD */
/* TXD_FLAG_END   (0x0001): 패킷 마지막 BD */
/* TXD_FLAG_TSO   (0x0800): TSO 사용 (mss/hdr_len 유효) */
/* TXD_FLAG_VLAN  (0x0400): VLAN 태그 삽입 지시 */

RX Descriptor 모드: tg3는 Standard RX 링(단일 SKB 버퍼, 일반 패킷)과 Jumbo RX 링(페이지 기반 분산 버퍼, 점보 프레임)을 분리 운영합니다. Standard 링의 기본 버퍼 크기는 1536바이트이며, Jumbo 링은 9KB 이상의 패킷을 여러 페이지로 분할하여 수신합니다.

APE(Application Processing Engine) FW 상호작용

BCM5719/5720의 APE는 내장 ARM 코어로, NC-SI 프로토콜 처리와 BMC 통신을 전담합니다. 호스트 드라이버와 APE는 공유 메모리 영역을 통해 통신하며, 동기화를 위해 spinlock 계열의 APE lock을 사용합니다.

/* tg3 APE 락 메커니즘 (drivers/net/ethernet/broadcom/tg3.c) */
static int tg3_ape_lock(struct tg3 *tp, int locknum)
{
    int i, off;
    u32 status;

    off = APE_LOCK_GRANT + 4 * locknum;
    /* 락 요청 */
    tg3_ape_write32(tp, APE_LOCK_REQ + 4 * locknum,
                    APE_LOCK_REQ_DRIVER);
    /* 최대 APE_LOCK_TIMEOUT 동안 대기 */
    for (i = 0; i < APE_LOCK_TIMEOUT; i++) {
        status = tg3_ape_read32(tp, off);
        if (status == APE_LOCK_GRANT_DRIVER)
            return 0;
        udelay(10);
    }
    return -EBUSY;
}

/* NC-SI: BMC가 동일 포트를 통해 네트워크에 접근하는 프로토콜 */
/* BMC 트래픽은 APE가 처리 → 호스트 ethtool 통계에 미포함 */

BMC 공유 MAC 필터링: 호스트 OS와 BMC가 같은 MAC 주소를 사용하므로, tg3 드라이버는 APE lock을 획득한 후 MAC 필터 테이블을 업데이트합니다. BMC 전용 멀티캐스트 그룹(NC-SI management)은 APE 펌웨어가 별도 관리하며, 호스트 드라이버의 필터 변경이 BMC 통신을 끊지 않도록 APE에 사전 통보합니다.

APE 이벤트 처리: 링크 상태 변화나 시스템 종료 시 tg3 드라이버는 APE에 이벤트를 알립니다. tg3_ape_driver_state_change()가 이를 처리하며, ACPI S5(soft off) 상태에서도 BMC의 WoL(Wake-on-LAN) 기능이 유지됩니다.

tg3 트러블슈팅 가이드

EEE 링크 드롭 (BCM57766): BCM57766을 포함한 일부 BCM 기가비트 NIC은 EEE(Energy Efficient Ethernet) 협상 시 스위치 호환성 문제로 링크가 간헐적으로 드롭됩니다. 증상은 수 분~수십 분 간격으로 링크가 재협상되며, dmesgtg3 eth0: Link is downLink is up이 반복됩니다.

TSO 체크섬 오류: ethtool -Stx_collisions 카운터는 실제 충돌이 아닌 내부 재시도를 의미하는 경우가 있습니다. TSO 관련 체크섬 오류는 tx_errors 증가로 나타나며, 특정 펌웨어 버전에서 TSO를 비활성화하면 해결됩니다.

PHY 자동협상 실패: MDI/MDIX 자동감지가 실패하는 환경(일부 구형 스위치)에서는 강제 속도 설정이 필요합니다. autoneg off 설정 시 MDI/MDIX도 수동으로 지정해야 합니다.

# EEE 링크 드롭 증상 확인
dmesg | grep -E 'tg3|eth0' | grep -E 'up|down' | tail -20

# EEE 비활성화 (BCM57766 링크 드롭 워크어라운드)
ethtool --set-eee eth0 eee off
# 영구 적용: /etc/NetworkManager/dispatcher.d/ 또는 udev rule 사용

# TSO 체크섬 오류 확인 및 비활성화
ethtool -S eth0 | grep -E 'tx_errors|tx_collisions'
ethtool -K eth0 tso off
# 체크섬 오류가 줄어드는지 확인
ethtool -S eth0 | grep tx_errors

# PHY 강제 속도 설정 (자동협상 실패 시)
ethtool -s eth0 speed 1000 duplex full autoneg off
# MDI 모드 강제 (크로스 케이블 연결 시)
ethtool -s eth0 mdix on

# APE 펌웨어 상태 및 NC-SI 버전 확인
ethtool -i eth0
# firmware-version: 5719-v1.46 NCSI v1.5.18.0 (APE FW 버전 포함)

# tg3 통계 전체 덤프 (APE/WoL 관련 포함)
ethtool -S eth0 | grep -E 'ape|wol|eee|phy'

Marvell/NXP 계열: mvneta / mvpp2 / enetc / dpaa2

Marvell과 NXP는 임베디드 및 산업용 네트워킹에서 강력한 입지를 가지고 있습니다. Marvell의 Armada 시리즈는 네트워크 어플라이언스에, NXP의 Layerscape/QorIQ 시리즈는 산업용 TSN 및 데이터 플레인 가속에 널리 사용됩니다.

드라이버 SoC/칩셋 최대 속도 패킷 프로세서 XDP TSN 소스 경로
mvneta Armada 370/38x/XP 2.5 GbE PPv1 (기본) O X drivers/net/ethernet/marvell/mvneta.c
mvpp2 Armada 7K/8K, CN9130 10 GbE PPv2.2 O X drivers/net/ethernet/marvell/mvpp2/
enetc NXP LS1028A 2.5 GbE ENETC SI O O (802.1Qbv/Qci/Qbu) drivers/net/ethernet/freescale/enetc/
dpaa2-eth NXP LS2088A/LX2160A 100 GbE WRIOP O X drivers/net/ethernet/freescale/dpaa2/

mvneta / mvpp2: Marvell 패킷 프로세서

Marvell Armada SoC의 네트워크 서브시스템은 PPv2(Packet Processor v2)라는 하드웨어 패킷 처리 엔진을 내장하고 있습니다. PPv2는 파싱, 분류, 폴리싱, RSS를 하드웨어에서 수행하여 CPU 부하를 최소화합니다.

Port GMAC/ XLMAC Parser L2 헤더 파싱 L3/L4 식별 VLAN 처리 PPPoE/DSA 커스텀 프로토콜 (TCAM 기반) Classifier C2 엔진 (TCAM 룩업) Flow ID 생성 QoS 마킹 DSCP→TC 매핑 Policer Token Bucket CIR/CBS 설정 초과 시 Drop RSS 해시 계산 5-tuple 기반 Indirection Table RXQ 0 RXQ 1 RXQ 2..N DMA BD Ring Scatter Gather CPU NAPI Poll BM Pool (Buffer Manager) Long Pool (2K) Short Pool (512) Jumbo Pool (10K) 하드웨어 버퍼 할당/해제 — CPU 개입 최소화 버퍼 공급
그림 ndd-33. mvpp2 하드웨어 파이프라인 — Parser → Classifier → Policer → RSS → RXQ → DMA → CPU, BM Pool이 버퍼 관리
비교 항목 mvneta (PPv1) mvpp2 (PPv2.2)
지원 SoC Armada 370, 38x, XP Armada 7K/8K, CN9130
최대 포트 수 3 (SoC당) 4 (CP당, 다중 CP 지원)
최대 속도 2.5 GbE 10 GbE (SFI/RXAUI)
하드웨어 파서 기본 L2-L4 파싱 프로그래머블 TCAM 파서
Classifier 단순 5-tuple 해시 C2 엔진 (TCAM + 해시)
Buffer Manager 소프트웨어 관리 하드웨어 BM (3개 Pool)
XDP 지원 O (4.19+) O (5.4+)
phylink 통합 O O (PHY 모드 동적 전환)
TSO 지원 O O
per-CPU RXQ O (소프트웨어 분배) O (하드웨어 RSS)

phylink 통합: mvpp2는 phylink 프레임워크를 통해 PHY 모드(SGMII, RGMII, SFI, RXAUI)를 런타임에 동적으로 전환합니다. SFP 모듈 삽입 시 자동으로 적절한 PHY 모드를 선택하며, Device Tree의 phy-mode 속성이 기본값을 결정합니다.

/* Device Tree 예시: mvpp2 포트 설정 */
&cp0_ethernet {
    status = "okay";
};

&cp0_eth0 {
    status = "okay";
    phy-mode = "10gbase-r";     /* SFI/10G 모드 */
    managed = "in-band-status";  /* phylink in-band AN */
    sfp = <&sfp_cp0_eth0>;      /* SFP 케이지 연결 */
};

&cp0_eth1 {
    status = "okay";
    phy-mode = "sgmii";
    phy = <&cp0_phy0>;          /* Copper PHY 연결 */
};

/* BM Pool 설정 (커널 코드) */
#define MVPP2_BM_LONG_BUF_SIZE   2048
#define MVPP2_BM_SHORT_BUF_SIZE  512
#define MVPP2_BM_POOL_SIZE_MAX   8192
# mvpp2 인터페이스 정보 확인
ethtool -i eth0
# driver: mvpp2, firmware-version: N/A

# RSS 설정 (mvpp2)
ethtool -X eth0 hfunc toeplitz
ethtool -N eth0 rx-flow-hash tcp4 sdfn

# XDP 프로그램 로드
ip link set dev eth0 xdp obj xdp_prog.o sec xdp

# mvpp2 코얼레싱
ethtool -C eth0 rx-usecs 64 rx-frames 32

# PHY/SFP 상태 확인
ethtool eth0
ethtool -m eth0          # SFP 모듈 EEPROM 정보

ENETC: IEEE TSN 지원

NXP LS1028A SoC에 내장된 ENETC(Enhanced Network Controller)는 리눅스 커널에서 가장 포괄적인 IEEE TSN(Time-Sensitive Networking) 지원을 제공합니다. 802.1Qbv(시간 인식 스케줄링), 802.1Qci(스트림 필터), 802.1Qbu(프레임 프리엠션)를 하드웨어 수준에서 구현합니다.

IEEE 1588 PTP 시간 기준 (gPTP Grandmaster 동기화) 나노초 정밀도 — 모든 TSN 기능의 시간 참조 Traffic Classes TC 7 (최고 우선순위) 제어 트래픽 TC 6 (Scheduled) 실시간 데이터 TC 5 (Reserved) AV 스트림 TC 4..1 TC 0 (Best Effort) 일반 트래픽 802.1Q 우선순위 매핑 Gate Control List (802.1Qbv Time-Aware Shaper) Slot 0: 0~125us Gate: TC7=O TC6=O 나머지=C Slot 1: 125~500us Gate: TC6=O TC5=O 나머지=C Slot 2: 500~875us Gate: TC5=O TC4..0=O TC7,6=C Slot 3: 875~1000us Gate: 전체 Open (BE) 주기: 1ms (1000us) O = Open, C = Closed 802.1Qci Stream Filter Stream ID 매칭 Gate Check Rate Policer 802.1Qbu Frame Preemption Express: TC7,6 Preemptable: TC0-5 TX MAC eMAC (express) pMAC (preempt) Wire 2.5 GbE
그림 ndd-34. ENETC TSN 스케줄링 — 802.1Qbv Gate Control List가 시간 슬롯별로 TC 게이트를 제어, Qbu 프리엠션 연동
TSN 기능 IEEE 표준 ENETC 지원 리눅스 인터페이스
시간 인식 스케줄링 802.1Qbv O (최대 256 GCL 엔트리) tc-taprio
스트림 필터/게이트 802.1Qci (PSFP) O (최대 2048 엔트리) tc-flower + tc-gate
프레임 프리엠션 802.1Qbu / 802.3br O ethtool --set-frame-preemption
Credit-Based Shaper 802.1Qav O tc-cbs
정밀 시간 동기화 IEEE 1588v2 / 802.1AS O (하드웨어 타임스탬프) ptp4l / phc2sys
경로 예약 802.1Qcc (SRP) 소프트웨어 지원 사용자 공간 데몬
# 802.1Qbv: taprio qdisc 설정 (시간 인식 스케줄링)
tc qdisc replace dev eno0 parent root handle 100 taprio \
  num_tc 4 \
  map 0 0 1 1 2 2 3 3 0 0 0 0 0 0 0 0 \
  queues 1@0 1@1 1@2 1@3 \
  base-time 1000000000 \
  sched-entry S 0x08 125000 \
  sched-entry S 0x04 375000 \
  sched-entry S 0x03 500000 \
  flags 0x02                # 0x02 = 하드웨어 오프로드

# taprio 상태 확인
tc qdisc show dev eno0

# 802.1Qci: PSFP 스트림 필터 설정
tc qdisc add dev eno0 clsact
tc filter add dev eno0 ingress protocol 802.1Q \
  flower vlan_prio 6 dst_mac 01:00:5e:00:01:01 \
  action gate index 1 \
    sched-entry open  125000 -1 \
    sched-entry close 875000 -1

# 802.1Qbu: 프레임 프리엠션 활성화
ethtool --set-frame-preemption eno0 \
  fp on \
  preemptible-queues-mask 0x0f \
  min-frag-size 60

# PTP 하드웨어 타임스탬프 설정
ptp4l -i eno0 -H -2 --step_threshold=1 &
phc2sys -s eno0 -c CLOCK_REALTIME -O 0 &

TSN 디버깅: tc -s qdisc show dev eno0로 각 TC별 전송 통계를 확인하고, ethtool --show-frame-preemption eno0로 프리엠션 상태를 점검합니다. 또한 PTP 동기화 정밀도는 pmc -u -b 0 'GET CURRENT_DATA_SET'으로 offset 값을 모니터링합니다.

dpaa2 아키텍처: WRIOP/DPIO 객체 모델

NXP의 DPAA2(Data Path Acceleration Architecture 2)는 LX2160A 등 고성능 Layerscape SoC에서 사용되는 정교한 네트워크 가속 프레임워크입니다. 기존의 고정 하드웨어 블록 대신, 소프트웨어 정의 객체 모델을 채택하여 유연한 데이터 플레인 구성을 가능하게 합니다.

DPAA2의 핵심은 MC(Management Complex) 펌웨어가 관리하는 객체(object) 시스템입니다. 각 객체는 독립된 하드웨어 리소스를 추상화하며, DPRC(Data Path Resource Container) 안에서 그룹화됩니다.

MC (Management Complex) Firmware 객체 생성 / 연결 / 수명 관리 — restool 또는 DPL(Data Path Layout)로 설정 DPRC (Root Container) — dprc.1 리눅스 커널 fsl-mc 버스 드라이버가 관리 DPRC (Child) — dprc.2 VM/Container 할당 가능 DPNI.0 Network Interface → eth0 DPNI.1 Network Interface → eth1 DPIO.0 QBMan Portal CPU 0 DPIO.1 QBMan Portal CPU 1 DPBP.0 Buffer Pool 2048 buffers DPMAC.1 MAC/PHY 25 GbE DPMAC.2 MAC/PHY 10 GbE DPCON.0 Concentrator (CH) DPSW.0 L2 Switch (선택 사항) DPNI.2 VM eth0 DPIO.2 vCPU Portal DPBP.1 VM 버퍼풀
그림 ndd-35. dpaa2 객체 모델 — MC 펌웨어가 DPRC 컨테이너 안에서 DPNI/DPIO/DPBP/DPMAC 객체를 관리

데이터 경로에서 패킷은 WRIOP(Packet I/O Engine)이 처리하고, QBMan(Queue-Based Manager) 포털을 통해 CPU와 데이터를 교환합니다.

Network Wire DPMAC SerDes Lane phylink L1 처리 WRIOP (Packet I/O Engine) Parser (L2-L4) Distribution (RSS) Policer/Shaper Order Restoration DPNI Flow Table TX/RX Queues QoS Table QBMan Queue Manager Frame Queue Buffer Pool Channel (CDAN) Enqueue/Dequeue Lock-free 디자인 DPIO QBMan Portal MMIO Region per-CPU 바인딩 CPU dpaa2-eth 드라이버 NAPI + XDP RX FQ MMIO CDAN IRQ TX Enqueue DPBP Buffer Pool 객체
그림 ndd-36. dpaa2 데이터 경로 — DPMAC → WRIOP → DPNI → QBMan → DPIO Portal → CPU, 각 단계가 독립 객체

QBMan 포털과 CDAN: 각 CPU는 전용 DPIO(QBMan 소프트웨어 포털)를 통해 프레임 큐에 접근합니다. CDAN(Channel Data Available Notification) 인터럽트가 새 패킷 도착을 알리면, NAPI 폴링이 시작됩니다. 이 구조는 CPU 간 락 경합 없이 병렬 패킷 처리를 가능하게 합니다.

# restool을 이용한 dpaa2 객체 관리

# 현재 객체 목록 조회
restool dprc show dprc.1
restool dpni info dpni.0
restool dpmac info dpmac.1

# DPNI 객체 생성 (네트워크 인터페이스)
restool dpni create \
  --num-tcs=8 \
  --num-queues=8 \
  --options=DPNI_OPT_HAS_KEY_MASKING

# DPIO 객체 생성 (per-CPU 포털)
restool dpio create \
  --channel-mode=DPIO_LOCAL_CHANNEL \
  --num-priorities=8

# DPBP 객체 생성 (버퍼 풀)
restool dpbp create

# 객체를 컨테이너에 연결
restool dprc assign dprc.1 --object=dpni.0 --plugged=1
restool dprc assign dprc.1 --object=dpio.0 --plugged=1

# DPNI와 DPMAC 연결 (L2 바인딩)
restool dprc connect dprc.1 \
  --endpoint1=dpni.0 \
  --endpoint2=dpmac.1

# 자식 DPRC 생성 (VM 할당용)
restool dprc create dprc.1 \
  --options=DPRC_CFG_OPT_ALLOC_ALLOWED
# dpaa2-eth 드라이버 운영

# 인터페이스 정보
ethtool -i eth0
# driver: dpaa2-eth
# firmware-version: 10.28.0

# 채널/큐 설정
ethtool -l eth0
ethtool -L eth0 combined 8

# RSS 설정
ethtool -X eth0 hfunc toeplitz
ethtool -N eth0 rx-flow-hash tcp4 sdfn

# XDP 프로그램 로드
ip link set eth0 xdp obj dpaa2_xdp.o sec xdp_prog

# WRIOP 통계 확인
ethtool -S eth0 | grep -E 'rx_|tx_|ch_'

# fsl-mc 버스 디바이스 확인
ls /sys/bus/fsl-mc/devices/
cat /sys/bus/fsl-mc/devices/dpni.0/driver

# DPL(Data Path Layout) 적용
# U-Boot에서: fsl_mc apply dpl 0x80100000

dpaa2 성능 최적화: LX2160A에서 최대 성능을 위해서는 DPIO 객체를 각 CPU 코어에 1:1 할당하고, DPNI의 큐 수를 CPU 수와 일치시킵니다. 또한 DPNI_OPT_HAS_KEY_MASKING 옵션을 활성화하면 RSS 해시 키 커스터마이징이 가능하여 워크로드별 최적 분배를 달성할 수 있습니다. Huge page(2MB)를 DPBP 버퍼 풀에 사용하면 TLB 미스를 줄여 추가 성능 향상을 얻습니다.

Realtek/Aquantia: r8169 / r8125 / atlantic

Realtek은 가장 널리 보급된 소비자/임베디드용 NIC 칩셋 제조사이며, Aquantia(현 Marvell 인수)는 멀티기가비트(2.5G/5G/10G) 시장을 선도합니다. 리눅스 커널에서 이 세 드라이버는 각기 다른 하드웨어 세대와 성능 계층을 담당합니다.

드라이버 주요 칩셋 최대 속도 큐 수 NAPI XDP HW Offload 커널 트리
r8169 RTL8111B~H, RTL8168 1 Gbps 1 O X VLAN, Checksum in-tree
r8169 (2.5G) RTL8125B/BG 2.5 Gbps 1 O X VLAN, Checksum, TSO in-tree
r8125 (벤더) RTL8125B/BG 2.5 Gbps 최대 4 O X VLAN, Checksum, TSO, RSS out-of-tree
atlantic AQC107/108/113 10 Gbps 최대 8 O O RSS, Checksum, TSO, LRO in-tree

r8169 드라이버 심화

r8169 드라이버(drivers/net/ethernet/realtek/r8169_main.c)는 커널에서 가장 많은 하드웨어 변형(quirk)을 처리하는 NIC 드라이버 중 하나입니다. 20년 이상의 칩 세대를 단일 드라이버로 지원하며, 각 변형마다 고유한 버그 회피 코드가 존재합니다.

r8169 TX/RX 데이터 경로 TX 경로 Application Socket qdisc (single TXQ) TX DMA Ring 256 desc PCI DMA PCIe Bus RTL Chip PHY RX 경로 PHY RTL Chip RX DMA Ring 256 desc NAPI Poll budget=64 GRO 병합 napi_gro_receive netif_receive_skb Socket IRQ Coalescing IntrMask: RxOK|TxOK 첫 IRQ→NAPI 스케줄
그림 37. r8169 TX/RX 데이터 경로 — 단일 큐 구조에서 NAPI 폴링 기반 수신 처리
RTL 칩 변형별 특성 및 quirk 테이블
칩셋 PCIe Gen 최대 속도 Jumbo (MTU) WoL 주요 quirk / 하드웨어 버그
RTL8111B 1.0 x1 1 Gbps 4K O TX timeout 빈발, PLL 초기화 지연 필요
RTL8111C 1.0 x1 1 Gbps 6K O ASPM L1 진입 시 링크 불안정
RTL8111D 2.0 x1 1 Gbps 9K O RX FIFO 오버플로우 시 칩 리셋 필요
RTL8111E 2.0 x1 1 Gbps 9K O EEE(Energy Efficient Ethernet) 호환성 문제
RTL8111F 2.1 x1 1 Gbps 9K O dash 관리 엔진 충돌, S5 WoL 실패 보고
RTL8111G 2.1 x1 1 Gbps 9K O 특정 BIOS에서 PCI config space 손상
RTL8111H 2.1 x1 1 Gbps 9K O 최신 리비전, quirk 최소화
RTL8168 1.0 x1 1 Gbps 4K O RTL8111 모바일 변형, 동일 quirk 공유
RTL8125B 2.1 x1 2.5 Gbps 9K O r8169 모듈에서 지원, RSS 미지원(단일 큐)
ASPM 상호작용 문제: RTL8111C/D 칩셋에서 ASPM(Active State Power Management) L1 상태 진입 시 링크가 불안정해지는 현상이 보고됩니다. r8169 드라이버는 이를 감지하면 자동으로 ASPM을 비활성화하지만, BIOS/ACPI가 다시 활성화하는 경우가 있습니다. 커널 부트 파라미터 pcie_aspm=off로 완전히 비활성화하거나, pcie_aspm.policy=performance를 사용할 수 있습니다.
전원 관리 및 진단
# r8169 드라이버 사용 중인 장치 확인
lspci -k | grep -A3 -i realtek

# 현재 칩 리비전 확인 (dmesg에서)
dmesg | grep r8169
# 예: r8169 0000:03:00.0: RTL8111H at 0xffffa1234000, ...

# ASPM 상태 확인
lspci -vvs 03:00.0 | grep -i aspm

# WoL(Wake-on-LAN) 설정 확인 및 활성화
ethtool -s eth0 wol g
ethtool eth0 | grep Wake-on

# EEE 상태 확인 (RTL8111E 이후)
ethtool --show-eee eth0

# TX timeout 발생 시 통계 확인
ethtool -S eth0 | grep -E "tx_timeout|rx_missed"

# 인터럽트 coalescing 조정 (rx-usecs/tx-usecs)
ethtool -C eth0 rx-usecs 100 tx-usecs 200
RX 링 오버플로우 진단

r8169 드라이버의 하드웨어적 한계 중 하나는 256개 디스크립터 단일 큐 구조입니다. RTL811x ASIC는 TX/RX 각각 최대 256개 디스크립터를 지원하며, 멀티 큐 확장 없이 단일 링으로 동작합니다. 이는 높은 패킷 수신률 환경에서 링 오버플로우를 유발할 수 있습니다.

RTL ASIC 백프레셔 부재: RTL811x/RTL8125 ASIC는 RX FIFO가 가득 찰 때 PHY 레벨 흐름제어(pause frame)를 자동으로 발생시키지 않습니다. 결과적으로 FIFO 오버플로우가 발생하면 패킷이 조용히 드롭됩니다.

ethtool -S로 확인할 수 있는 두 카운터의 의미는 다릅니다:

링 오버플로우를 실시간으로 진단하려면 bpftracer8169_poll() 함수의 per-poll 패킷 수를 히스토그램으로 관찰할 수 있습니다:

# bpftrace: r8169_poll() 호출당 처리 패킷 수 히스토그램
bpftrace -e '
kretprobe:r8169_poll {
  @hist = hist(retval);
}
interval:s:5 {
  print(@hist);
  clear(@hist);
}'

# rx_missed_errors 실시간 모니터링 (1초 간격)
watch -n1 "ethtool -S eth0 | grep -E 'rx_missed|rx_fifo|rx_packets'"

# 링 오버플로우 경향 확인 (누적 delta)
prev=0
while sleep 1; do
  cur=$(ethtool -S eth0 | awk '/rx_missed_errors/{print $2}')
  echo "delta: $((cur - prev))  total: $cur"
  prev=$cur
done

# rx-usecs 증가로 NAPI 빈도 조정 (배치 크기 확대)
ethtool -C eth0 rx-usecs 200

# 링 크기 확인 (최대 256, 기본 256)
ethtool -g eth0

오버플로우 발생 시 근본 해결책은 다음 순서로 시도합니다: (1) rx-usecs 증가로 인터럽트 코얼레싱 강화 → (2) 트래픽 부하 자체를 줄이거나 QoS로 입력 속도 제한 → (3) 근본적으로 멀티 큐를 지원하는 NIC(r8125 2.5GbE 또는 인텔 igc)로 교체.

링크 드롭/지연 의심 ethtool -S eth0 rx_missed_errors 증가? YES HW FIFO 오버플로우 확인됨 NO 다른 원인 조사 (케이블, 드라이버 버그 등) rx_fifo_errors도 증가? YES 링 오버플로우 확진 rx-usecs 증가 (코얼레싱 강화) QoS로 입력 속도 제한 (tc qdisc) 멀티 큐 NIC 교체 (igc, r8125)
r8169 RX Ring Overflow 진단 플로우차트 — rx_missed_errors → rx_fifo_errors 순서로 확인 후 해결책 선택
r8169와 BQL(Byte Queue Limits) 상호작용

BQL(Byte Queue Limits)은 TX 링의 인플라이트 바이트 수를 동적으로 조정하여 레이턴시를 낮추는 커널 메커니즘입니다. 단일 큐 드라이버인 r8169에서는 BQL의 중요성이 멀티 큐 드라이버보다 더 높습니다. 여러 큐로 분산할 수 없기 때문에 단일 TX 링의 인플라이트 제어가 전체 레이턴시를 결정합니다.

BQL 파라미터는 sysfs를 통해 조회하고 조정할 수 있습니다:

# BQL 현재 상태 조회
cat /sys/class/net/eth0/queues/tx-0/byte_queue_limits/limit
cat /sys/class/net/eth0/queues/tx-0/byte_queue_limits/limit_min
cat /sys/class/net/eth0/queues/tx-0/byte_queue_limits/limit_max
cat /sys/class/net/eth0/queues/tx-0/byte_queue_limits/hold_time

# BQL 인플라이트 바이트 수 확인
cat /sys/class/net/eth0/queues/tx-0/byte_queue_limits/inflight

# BQL limit_max 수동 조정 (낮출수록 레이턴시 감소, 높일수록 처리량 증가)
echo 10000 > /sys/class/net/eth0/queues/tx-0/byte_queue_limits/limit_max

fq_codel과의 시너지: tc qdisc로 fq_codel을 설정하면 BQL과 상호 보완적으로 동작합니다. BQL은 드라이버 TX 링 레벨에서 인플라이트 바이트를 제한하고, fq_codel은 그 위 qdisc 레이어에서 per-flow 공정성과 지연 기반 드롭을 수행합니다. 단일 큐 NIC에서 버퍼블로트를 완화하는 가장 효과적인 조합입니다:

# fq_codel 적용 (단일 큐 NIC 버퍼블로트 완화)
tc qdisc replace dev eth0 root fq_codel

# 상태 확인
tc qdisc show dev eth0
tc -s qdisc show dev eth0  # 통계 포함

# 파라미터 조정 (target: 목표 대기시간, interval: 측정 구간)
tc qdisc replace dev eth0 root fq_codel target 5ms interval 100ms
quirk 아키텍처 분석

r8169 드라이버의 핵심 설계 원칙은 칩별 quirk 함수 분리입니다. 각 RTL 칩 리비전마다 하드웨어 초기화 루틴이 다르기 때문에, 드라이버는 rtl_chip_infos[] 배열을 통해 칩별 특성을 등록하고 해당 rtl_hw_start_*() 함수 포인터를 호출합니다.

/* drivers/net/ethernet/realtek/r8169_main.c (발췌/단순화) */

/* 칩 정보 구조체 */
struct rtl_chip_info {
    const char *name;
    u8          mcfg;           /* MAC 설정 레지스터 값 */
    u32         RxConfigMask;
    void        (*hw_start)(struct rtl8169_private *tp);
};

/* 칩별 초기화 함수 테이블 (일부) */
static const struct rtl_chip_info rtl_chip_infos[] = {
    [RTL_GIGA_MAC_VER_02] = {
        .name         = "RTL8169s",
        .hw_start     = rtl_hw_start_8169,
    },
    [RTL_GIGA_MAC_VER_07] = {
        .name         = "RTL8102e",
        .hw_start     = rtl_hw_start_8102e,
    },
    [RTL_GIGA_MAC_VER_46] = {
        .name         = "RTL8168H",
        .hw_start     = rtl_hw_start_8168h,
    },
    [RTL_GIGA_MAC_VER_63] = {
        .name         = "RTL8125B",
        .hw_start     = rtl_hw_start_8125b,
    },
    /* ... 50개 이상의 칩 버전 엔트리 ... */
};

/* 초기화 시 칩 버전 감지 및 함수 포인터 등록 */
static void rtl_init_one(struct rtl8169_private *tp)
{
    tp->hw_start = rtl_chip_infos[tp->mac_version].hw_start;
}

ASPM L1 조건부 비활성화: 일부 칩(RTL8111B/C 등)은 ASPM L1 절전 상태에서 복귀 시 링크 불안정 현상이 보고되어 있습니다. 드라이버는 칩 버전을 참조하여 오래된 리비전에서 ASPM을 자동으로 비활성화합니다:

/* ASPM quirk 탐지 흐름 */
static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable)
{
    /* RTL8411B(VER_46) 이전 칩은 ASPM L1 활성화 금지 */
    if (tp->mac_version < RTL_GIGA_MAC_VER_46)
        return;

    if (enable) {
        RTL_W8(tp, Config2, RTL_R8(tp, Config2) | ClkReqEn);
        RTL_W8(tp, Config5, RTL_R8(tp, Config5) | ASPM_en);
    } else {
        RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~ClkReqEn);
        RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~ASPM_en);
    }
}

/* PLL 타이밍 quirk: RTL8111B/C는 PLL off 시 복귀 지연 보정 필요 */
static void rtl8168b_hw_start(struct rtl8169_private *tp)
{
    RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en);
    /* PLL이 꺼진 상태에서의 wake-up 타이밍 보정 */
    RTL_W16(tp, CPlusCmd, RTL_R16(tp, CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK);
}
quirk 수동 확인 방법: dmesg | grep r8169로 드라이버가 감지한 칩 버전을 확인하고, 커널 소스 drivers/net/ethernet/realtek/r8169_main.c에서 해당 RTL_GIGA_MAC_VER_* 인덱스의 hw_start 함수를 직접 추적할 수 있습니다.
커널 트레이싱 레시피

r8169 poll 경로의 성능을 분석하기 위한 ftrace/bpftrace/perf 레시피 모음입니다.

## 1. ftrace function_graph: r8169 poll 경로 전체 추적
echo function_graph > /sys/kernel/debug/tracing/current_tracer
echo 'r8169_poll' > /sys/kernel/debug/tracing/set_graph_function
echo 1 > /sys/kernel/debug/tracing/tracing_on
sleep 1
echo 0 > /sys/kernel/debug/tracing/tracing_on
cat /sys/kernel/debug/tracing/trace | head -60

## 2. bpftrace: per-poll 처리 패킷 수 히스토그램 (10초 수집)
bpftrace -e '
kretprobe:r8169_poll {
  @pkt_hist = hist(retval);
}
END { print(@pkt_hist); }
' &
sleep 10 && kill %1

## 3. perf stat: softirq NET_RX 오버헤드 측정
perf stat -e irq:softirq_entry,irq:softirq_exit \
  --filter 'vec == 3' \
  -a sleep 5

## 4. 드라이버 함수 레이턴시 분포 (bpftrace)
bpftrace -e '
kprobe:r8169_poll   { @start[tid] = nsecs; }
kretprobe:r8169_poll {
  $lat = nsecs - @start[tid];
  @lat_us = hist($lat / 1000);
  delete(@start[tid]);
}
interval:s:5 { print(@lat_us); clear(@lat_us); }'

주요 트레이스포인트 목록:

트레이스포인트 / kprobe 용도 비고
kprobe:r8169_poll NAPI poll 진입/복귀, 패킷 수 측정 retval = 처리한 패킷 수
net:netif_receive_skb 커널 스택 전달 시점 tracepoint
net:net_dev_xmit TX 큐잉 시점 tracepoint
irq:softirq_entry vec==3 NET_RX softirq 진입 vec 3 = NET_RX_SOFTIRQ
kprobe:rtl8169_tx_timeout TX 타임아웃 발생 추적 행 발생 시 경보 설정 가능

r8125 / RTL8125B: 2.5GbE 심화

RTL8125B 칩셋은 2.5 Gbps 이더넷을 지원하는 소비자용 NIC입니다. 커널 5.9 이후 r8169 모듈이 기본 지원하지만, Realtek 공식 r8125 out-of-tree 드라이버와 기능 차이가 있습니다.

in-tree(r8169) vs out-of-tree(r8125) 비교
항목 r8169 (in-tree) r8125 (Realtek 벤더)
멀티큐 RSS 미지원 (단일 큐) 최대 4개 큐 지원
TSO (TCP Segmentation Offload) 지원 지원
USO (UDP Segmentation Offload) 미지원 지원
커널 버전 호환성 커널 빌드에 포함 별도 컴파일 필요, DKMS 지원
유지보수 커널 커뮤니티 관리 Realtek 자체 업데이트
안정성 높음 (광범위 테스트) 중간 (특정 커널에서 빌드 실패 보고)
XDP 지원 미지원 미지원
Flow Control 기본 지원 고급 설정 가능
성능 (iperf3 단일 스트림) ~2.35 Gbps ~2.35 Gbps
성능 (iperf3 다중 스트림) ~2.35 Gbps (단일 큐 한계) ~2.40 Gbps (RSS 효과 미미)
어떤 드라이버를 사용할 것인가: 대부분의 사용 환경에서 in-tree r8169 드라이버로 충분합니다. 2.5 Gbps 단일 스트림 성능은 두 드라이버 간 차이가 미미하며, RSS 멀티큐가 필요한 고병렬 워크로드가 아니라면 커널 내장 드라이버를 권장합니다. DKMS 관리 부담 없이 커널 업그레이드 시 자동으로 호환성이 보장됩니다.
# 현재 사용 중인 드라이버 확인
ethtool -i eth0
# driver: r8169  또는  driver: r8125

# 2.5G 링크 속도 확인
ethtool eth0 | grep Speed
# Speed: 2500Mb/s

# out-of-tree r8125 설치 (필요 시)
tar xf r8125-9.012.04.tar.bz2
cd r8125-9.012.04
sudo ./autorun.sh

# r8169 블랙리스트 (out-of-tree 사용 시)
echo "blacklist r8169" | sudo tee /etc/modprobe.d/r8169-blacklist.conf
sudo depmod -a

atlantic 드라이버: Aquantia AQC107/108/113 심화

Aquantia(현 Marvell)의 AQC 시리즈는 임베디드 MIPS 프로세서를 탑재한 고성능 멀티기가비트 NIC입니다. 펌웨어가 MAC/PHY 초기화와 관리를 담당하며, 호스트 드라이버는 메일박스 인터페이스를 통해 펌웨어와 통신합니다. atlantic 드라이버(drivers/net/ethernet/aquantia/atlantic/)는 이 독특한 아키텍처를 반영합니다.

AQC 펌웨어 아키텍처 호스트 (x86/ARM) atlantic 드라이버 aq_nic.c aq_ring.c FW Mailbox Interface DMA Rings (TX/RX) PCIe Gen3 x4 AQC ASIC MIPS FW Core 펌웨어 엔진 Mailbox Regs Offload Engine Checksum/LSO RSS Engine Toeplitz Hash MAC (10G) PHY (10GBASE-T) MDI (RJ45) Flow Steering / EtherType Filter / VLAN Filter
그림 38. AQC 펌웨어 아키텍처 — MIPS 코어 기반 펌웨어와 호스트 드라이버 메일박스 통신 구조
AQC RSS 멀티큐 수신 흐름 Ingress Packet L2/L3/L4 Parser src/dst IP+Port 추출 RSS Hash (Toeplitz) 40-byte 비밀 키 → 32bit hash Queue Selector hash % N큐 인디렉션 테이블 RXQ 0 RXQ 1 RXQ 2 RXQ 3 RXQ N (최대 8큐) NAPI 0 NAPI 1 NAPI 2 NAPI 3 NAPI N
그림 39. AQC RSS 멀티큐 수신 흐름 — Toeplitz 해시 기반 큐 분배와 per-queue NAPI 인스턴스
펌웨어 버전 의존성
칩셋 최소 FW 버전 권장 FW 버전 주요 기능/수정
AQC107 1.5.44 3.1.100+ 10G 링크 안정성, WoL 수정, EEE 개선
AQC108 1.5.44 3.1.100+ 5G/2.5G 모드 안정성 개선
AQC113 4.0.0 4.6.x+ AQC113 전용 신규 칩, 초기 FW 버그 다수 수정
펌웨어 업데이트: AQC 시리즈의 펌웨어는 NIC 플래시 ROM에 저장되며, atlantic 드라이버의 ethtool --flash 명령이나 Marvell 공식 도구로 업데이트합니다. 펌웨어 버전이 너무 오래되면 드라이버가 경고 메시지를 출력하며 일부 기능을 비활성화합니다.
성능 튜닝
# 펌웨어 버전 확인
ethtool -i aqc0
# firmware-version: 3.1.100

# RSS 해시 키 및 인디렉션 테이블 확인
ethtool -x aqc0

# RSS 인디렉션 테이블 수정 (큐 0,1,2,3 균등 분배)
ethtool -X aqc0 equal 4

# RSS 해시 필드 설정 (TCP 4-tuple)
ethtool -N aqc0 rx-flow-hash tcp4 sdfn

# 링 버퍼 크기 증가
ethtool -G aqc0 rx 4096 tx 4096

# 인터럽트 coalescing 최적화
ethtool -C aqc0 adaptive-rx on adaptive-tx on

# IRQ affinity 설정 (CPU 코어 분산)
echo 1 > /proc/irq/50/smp_affinity  # RXQ0 → CPU0
echo 2 > /proc/irq/51/smp_affinity  # RXQ1 → CPU1
echo 4 > /proc/irq/52/smp_affinity  # RXQ2 → CPU2
echo 8 > /proc/irq/53/smp_affinity  # RXQ3 → CPU3

# Flow steering 규칙 추가 (특정 포트 → 특정 큐)
ethtool -N aqc0 flow-type tcp4 dst-port 80 action 0
ethtool -N aqc0 flow-type tcp4 dst-port 443 action 1

# 통계 확인
ethtool -S aqc0 | grep -E "Queue|rx_packets|tx_packets"
EEE(Energy Efficient Ethernet) 동작 원리

EEE(Energy Efficient Ethernet)는 IEEE 802.3az 표준으로 정의된 전력 절감 기술입니다. 트래픽이 없을 때 링크를 LPI(Low Power Idle) 상태로 전환하여 소비 전력을 줄입니다. AQC(Aquantia) MIPS 펌웨어는 링크 상태와 트래픽 활동을 감시하여 LPI 전환을 제어합니다.

IEEE 802.3az LPI 신호 교환 흐름은 다음과 같습니다: 송신측 MAC이 IDLE 상태를 감지하면 LPI Request 시그널을 PHY에 전달하고, PHY는 링크 파트너와 협의하여 링크를 저전력 모드로 전환합니다. 패킷 전송이 필요해지면 PHY가 링크를 재활성화(Wake)하는데, 이때 PHY 재훈련(retraining) 지연이 발생합니다.

AQC MIPS FW 상태 전환: Active → LPI Request → LPI → Wake → Active

속도별 LPI 이탈 시 wake-up 지연 시간:

링크 속도 Wake-up 지연 (Tw_sys_tx) 비고
100 Mbps 30 μs IEEE 802.3az-2010 표준값
1 Gbps 16.5 μs 표준값
2.5 Gbps 20 μs IEEE 802.3bz
5 Gbps 25 μs IEEE 802.3bz
10 Gbps 4.5 μs IEEE 802.3az-2010
EEE로 인한 링크 드롭 증상: EEE wake-up 지연 중 들어오는 패킷이 유실될 수 있습니다. 특히 레이턴시 민감한 실시간 애플리케이션이나 짧은 버스트 트래픽 환경에서 EEE가 간헐적 링크 드롭이나 TCP 재전송을 유발할 수 있습니다.
# EEE 현재 상태 확인
ethtool --show-eee aqc0

# EEE 전체 비활성화
ethtool --set-eee aqc0 eee off

# 특정 속도에서만 EEE 비활성화 (1G에서만 끄기)
ethtool --set-eee aqc0 advertise 0x0  # 모든 속도 EEE advertisement 제거

# EEE 관련 통계 (드라이버가 제공하는 경우)
ethtool -S aqc0 | grep -i eee
펌웨어 버전 매트릭스 확장

AQC NIC의 기능 지원 여부는 펌웨어 버전에 따라 달라집니다. 다음 표는 주요 FW 버전별 기능 매핑을 정리한 것입니다:

FW 버전 범위 추가된 주요 기능 알려진 이슈
1.x 기본 1G/2.5G/5G/10G 링크, RSS 4큐 EEE wake-up 지연 버그 (일부 보드)
2.x 멀티캐스트 필터링 개선, WoL 강화 메일박스 응답 시간 간헐적 지연
3.x XDP 지원, 16큐 RSS, FW watchdog 체크섬 오프로드 엣지 케이스
4.x EEE 안정화, PTP 하드웨어 타임스탬프 -

FW 장애 모드 및 대응:

# FW 버전 확인
ethtool -i aqc0 | grep firmware-version

# FW 관련 오류 메시지 조회
dmesg | grep -i "atlantic\|aqc\|firmware\|mailbox"

# FW 응답 상태 확인 (aq_hw_fw_state_check 관련 로그)
dmesg | grep -E "FW|fw_state|mailbox"

# NIC 재초기화로 FW 워치독 리셋 트리거 (링크 재연결)
ip link set aqc0 down && sleep 2 && ip link set aqc0 up
atlantic XDP 특성과 제약

atlantic 드라이버(drivers/net/ethernet/aquantia/atlantic/)는 XDP(eXpress Data Path)를 지원하지만 몇 가지 중요한 제약이 있습니다.

XDP 처리 경로: aq_ring_xdp_clean() 함수가 RX 링에서 패킷을 가져와 XDP 프로그램을 실행합니다. XDP_PASS 시 일반 커널 스택으로, XDP_DROP 시 즉시 드롭, XDP_TX 시 동일 링을 통해 재전송합니다.

AF_XDP zero-copy 미지원 이유: AQC NIC의 DMA 버퍼 할당은 MIPS FW가 제어합니다. FW가 DMA 버퍼 레이아웃을 관리하기 때문에 커널이 해당 메모리 페이지를 사용자공간과 직접 공유(zero-copy)하는 구조를 구현할 수 없습니다. AF_XDP zero-copy는 드라이버가 DMA 버퍼를 완전히 제어할 때만 가능합니다.

XDP_TX 링 재사용 제약: XDP_TX 액션은 동일 RX 링 버퍼를 TX에 재사용합니다. 이로 인해 높은 XDP_TX 비율에서는 RX 링이 소진되어 추가 패킷 수신이 지연될 수 있습니다.

XDP 액션별 성능 비교:

XDP 액션 처리 경로 성능 (10G 기준) 비고
XDP_DROP RX 링 → XDP 프로그램 → 드롭 ~14 Mpps 최고 처리량, SKB 할당 없음
XDP_PASS RX 링 → XDP → 커널 스택 ~8 Mpps SKB 할당 오버헤드
XDP_TX RX 링 → XDP → 동일 TX ~10 Mpps 링 공유로 인한 제약
커널 스택 드롭 RX → SKB 할당 → iptables DROP ~3 Mpps XDP_DROP 대비 ~4.7× 느림
Active 정상 링크 동작 LPI Request IDLE 감지, 신호 전송 LPI 저전력 유지 중 Wake PHY 재훈련 중 IDLE 감지 (트래픽 없음) 파트너 동의 TX 필요 재훈련 완료 취소 Wake-up 지연 (Tw_sys_tx) 100M: 30 μs 1G: 16.5 μs 2.5G: 20 μs 5G: 25 μs 10G: 4.5 μs PHY 재훈련 지연 링크 파트너와 협상 재시작 패킷 손실 가능 구간
EEE LPI 상태 전환 다이어그램 — Active↔LPI Request→LPI↔Wake 전환 및 속도별 wake-up 지연 시간

Chelsio: cxgb4

Chelsio Communications는 고성능 서버/데이터센터용 NIC 전문 제조사로, 하드웨어 TCP/IP 오프로드 엔진(TOE), RDMA, 암호화 가속을 내장한 프로토콜 오프로드 어댑터의 선구자입니다. cxgb4 드라이버(drivers/net/ethernet/chelsio/cxgb4/)는 Terminator 5/6(T5/T6) ASIC을 지원합니다.

드라이버 ASIC 최대 속도 큐 수 오프로드 엔진 커널 트리
cxgb4 T5 40 Gbps 최대 128 TOE, iSCSI, RDMA in-tree
cxgb4 T6 100 Gbps 최대 256 TOE, iSCSI, RDMA, TLS, DTLS in-tree

cxgb4 아키텍처 심화

Chelsio T6 ASIC은 단순한 NIC가 아닌, 전체 네트워크 프로토콜 스택을 하드웨어로 구현한 네트워크 프로세서입니다. 주요 구성 요소는 MPS(MAC/Port Switch), TP(TCP/IP Processing Engine), ULP(Upper Layer Protocol Engine), SGE(Scatter-Gather Engine), 그리고 암호화 가속기입니다.

Chelsio T6 ASIC 엔진 구조 100G Port ×2 MPS MAC Lookup VLAN Filter Port Switch Multicast Replication 512 MAC 엔트리 TP (TCP/IP Engine) Protocol Parsing TCP State Machine Checksum Verify RSS Hash Calc Flow Lookup TCAM Filter ───────── Retransmission Congestion Ctrl Segmentation Reassembly ULP Engine TOE iSCSI Offload RDMA (iWARP) NVMe-oF FCoE TLS Record Crypto Engine AES-GCM/CBC SHA-256/512 DTLS/TLS 1.3 SGE Scatter-Gather Engine TX/RX Queues Free List Mgmt DMA Coalescing Doorbell 최대 256 큐페어 PCIe IF Gen3 x16 128 Gbps SR-IOV → Host CPU 프로토콜 처리 오프로드 엔진 암호화 가속 DMA 엔진
그림 40. Chelsio T6 ASIC 엔진 구조 — MPS, TP, ULP, SGE, Crypto 엔진의 파이프라인 구성
T5 vs T6 비교
항목 T5 (Terminator 5) T6 (Terminator 6)
최대 포트 속도 40 Gbps (QSFP+) 100 Gbps (QSFP28)
PCIe 인터페이스 Gen3 x8 Gen3 x16
TX/RX 큐 수 최대 128 최대 256
TCP 동시 연결 (TOE) 최대 131K 최대 262K
RDMA (iWARP) 연결 최대 131K 최대 262K
암호화 가속 AES-128/256-CBC, SHA AES-GCM, TLS 1.2/1.3, DTLS
TLS Offload 미지원 지원 (인라인 TLS record)
TCAM 필터 수 496 2048
SR-IOV VF 수 최대 64 최대 256
패킷 레이트 (64B) ~60 Mpps ~148 Mpps
SGE(Scatter-Gather Engine) 모델

SGE는 호스트 CPU와 ASIC 사이의 DMA 전송을 관리하는 핵심 엔진입니다. 각 큐페어(Queue Pair)는 TX 큐, RX 큐(Ingress Queue), Free List(FL), 그리고 응답 큐(Response Queue)로 구성됩니다. 호스트 드라이버는 Doorbell 레지스터에 쓰기 연산을 수행하여 새로운 설명자를 ASIC에 통지합니다.

# cxgb4 드라이버 로드 상태 확인
lsmod | grep cxgb4

# ASIC 세대 및 포트 정보 확인
ethtool -i eth0
# driver: cxgb4
# firmware-version: 1.27.1.0

# SGE 큐 구성 확인
ethtool -l eth0
# Combined: 16 (현재) / 256 (최대)

# 큐 수 변경 (CPU 코어 수에 맞춤)
ethtool -L eth0 combined 32

# 링 버퍼 크기 조정
ethtool -G eth0 rx 8192 tx 8192

# 인터럽트 coalescing (지연 시간 vs 처리량 트레이드오프)
ethtool -C eth0 rx-usecs 10 rx-frames 64

# 드라이버 내부 통계 확인
ethtool -S eth0 | head -40

# PCIe 대역폭 확인
lspci -vvs $(ethtool -i eth0 | grep bus-info | awk '{print $2}') | grep -i width

cxgb4 오프로드 엔진

Chelsio의 핵심 차별점은 프로토콜 오프로드입니다. TOE(TCP Offload Engine)는 TCP 상태 머신 전체를 ASIC에서 실행하여 호스트 CPU 부하를 극적으로 줄이며, RDMA(iw_cxgb4)는 제로카피 데이터 전송을 가능하게 합니다.

커널 TCP 스택 vs TOE 오프로드 경로 비교 일반 커널 TCP 경로 Application (send/recv) Socket Layer TCP/IP Stack (커널) qdisc / TC Layer NIC Driver (cxgb4) T6 ASIC (SGE only) CPU 부하 높음 TOE 오프로드 경로 Application (send/recv) Socket Layer TOE Module (toecore) TCP/IP 바이패스 (커널 스택 우회) cxgb4 + tom (TOE driver) T6 ASIC (TP+ULP+SGE) HW TCP State Machine CPU 부하 낮음 모든 패킷 CPU 처리 데이터만 CPU 전달
그림 41. 커널 TCP 스택 vs TOE 오프로드 경로 — TOE는 TCP/IP 처리를 ASIC으로 완전히 오프로드
오프로드 성능 영향
모드 CPU 사용률 (100G) 처리량 지연 시간 동시 연결 수 적합 워크로드
일반 TCP (커널) 60~80% ~90 Gbps ~15 μs 제한 없음 범용
TOE 10~20% ~95 Gbps ~5 μs 최대 262K 대량 연결 서버
RDMA (iWARP) 5~10% ~98 Gbps ~2 μs 최대 262K 스토리지, HPC
TLS Offload (T6) 15~25% ~80 Gbps ~8 μs 최대 32K HTTPS 서버
TOE 사용 시 주의사항: TOE는 TCP 연결 상태를 ASIC 메모리에 유지하므로, 동시 연결 수에 하드웨어 제한이 있습니다. 또한 iptables/nftables 같은 커널 방화벽 규칙이 TOE 연결에 적용되지 않을 수 있어 보안 정책 검토가 필요합니다. 리눅스 메인라인 커널은 TOE를 공식 지원하지 않으며, Chelsio의 out-of-tree 패치(WD-TOE)가 필요합니다.
# RDMA (iw_cxgb4) 모듈 로드 확인
lsmod | grep iw_cxgb4

# RDMA 디바이스 목록
rdma link show

# RDMA 성능 테스트 (서버 측)
ib_write_bw -d cxgb4_0 --report_gbits

# RDMA 성능 테스트 (클라이언트 측)
ib_write_bw -d cxgb4_0 192.168.1.100 --report_gbits

# TLS offload 확인 (T6 전용)
ethtool -k eth0 | grep tls
# tls-hw-tx-offload: on
# tls-hw-rx-offload: on

# TLS offload 활성화
ethtool -K eth0 tls-hw-tx-offload on tls-hw-rx-offload on

# iSCSI offload 상태 확인
iscsiadm -m iface | grep cxgb4

# 오프로드 통계 확인
ethtool -S eth0 | grep -E "toe_|rdma_|tls_"
TOE 성능 분석

TOE는 모든 워크로드에서 이득을 주는 것이 아니다. 연결 수와 트래픽 패턴에 따라 이득과 손해가 달라진다.

워크로드TOE 효과이유
대규모 병렬 연결 (10K+) + 대용량 전송CPU -60%, 지연 -3×ASIC이 TCP 상태 머신 전담; 연결당 CPU 비용 거의 없음
고정 연결 수 + 스트리밍 (파일 서버)처리량 +5~10%TCP ACK 처리 오프로드 효과
소형 패킷 + 짧은 연결 (HTTP/1.0 스타일)중립 또는 소폭 손해ASIC 연결 설정 비용이 커널보다 높음
높은 연결 생성 속도 (connection churn)손해 가능TOE 연결 설정/해제 레이턴시가 SW보다 길 수 있음
방화벽/NAT 경유 트래픽iptables 미적용으로 보안 위험TOE 연결은 커널 netfilter 우회
Crypto 오프로드 파이프라인

Chelsio T6 ASIC은 TLS 인라인 암호화와 IPsec 오프로드를 지원한다. kTLS TX 경로에서 소켓 레이어가 TLS 레코드를 구성하면, T6 crypto engine이 와이어로 나가기 전 AES-GCM 암호화를 수행한다. 이 과정에서 CPU는 TLS 레코드 메타데이터만 처리하고, 실제 암호화 연산은 ASIC이 전담한다.

오프로드 유형처리량 (100G)CPU 절감최소 HW
TLS 1.2 TX (AES-128-GCM)~80 Gbps~50% 감소T6 (cxgb4)
TLS 1.3 TX (AES-256-GCM)~75 Gbps~45% 감소T6 (cxgb4)
IPsec ESP (AES-128-GCM)~90 Gbps~40% 감소T6 (cxgb4)
TLS RX (복호화)~70 Gbps~45% 감소T6 (cxgb4)
# kTLS TX 오프로드 cxgb4 활성화
ethtool -K eth0 tls-hw-tx-offload on

# kTLS 오프로드 상태 확인
ethtool -k eth0 | grep tls
# tls-hw-tx-offload: on
# tls-hw-rx-offload: on (T6 지원)

# TLS 오프로드 통계 확인
ethtool -S eth0 | grep tls_
# tls_tx_sw_fallback: 0   (SW 폴백 없으면 HW 오프로드 정상)
# tls_tx_hw_records: 1234567

# IPsec 오프로드 상태 확인
ip xfrm state list
ethtool -k eth0 | grep esp
# esp-hw-offload: on

# 오프로드 연결 수 확인
ethtool -S eth0 | grep -E "toe_conn|tls_conn"

cxgb4 패킷 분류 파이프라인

T6 ASIC은 하드웨어 기반 다단계 패킷 분류 파이프라인을 제공합니다. MPS(MAC Port Switch)에서 L2 필터링 후, TP(TCP/IP Engine)에서 프로토콜 매칭과 TCAM/해시 기반 필터를 적용하여 최종적으로 적절한 SGE 큐로 패킷을 전달합니다. 이 파이프라인은 tc flower 오프로드를 통해 소프트웨어적으로도 제어 가능합니다.

cxgb4 패킷 분류 파이프라인 Ingress Packet Wire/PHY MPS MAC Lookup (DMAC 매칭) VLAN Filter Promisc Mode Multicast Hash 512 MAC entries TP Protocol Parse L3/L4 Decode TCP/UDP 구분 Checksum 검증 RSS Hash 계산 Toeplitz/XOR Filter TCAM 매칭 (정확/와일드카드) Hash Filter Action 결정 Drop/Pass/Steer 2048 TCAM rules Queue Steering RSS 인디렉션 Filter 결과 큐 선택 결정 Priority 반영 → SGE Queues SGE Q0 SGE Q1 SGE Q... SGE QN DROP 분류 순서: 1. L2 (MPS) 2. L3/L4 (TP) 3. 필터 (TCAM) 4. 큐 배정
그림 42. cxgb4 패킷 분류 파이프라인 — MPS→TP→Filter→Queue Steering→SGE 다단계 하드웨어 분류
필터 설정 및 tc offload
# TCAM 필터: 특정 목적지 IP의 패킷을 큐 5로 전달
ethtool -N eth0 flow-type tcp4 dst-ip 10.0.0.100 action 5

# 설정된 필터 규칙 확인
ethtool -n eth0

# tc flower 오프로드 (하드웨어 가속 필터)
tc qdisc add dev eth0 ingress

# 소스 IP 기반 패킷 드롭 (하드웨어 오프로드)
tc filter add dev eth0 ingress protocol ip flower \
    src_ip 192.168.1.0/24 \
    skip_sw \
    action drop

# VLAN + 목적지 포트 기반 큐 스티어링
tc filter add dev eth0 ingress protocol 802.1Q flower \
    vlan_id 100 \
    dst_port 8080 \
    skip_sw \
    action skbedit queue_mapping 3

# 오프로드된 필터 통계 확인
tc -s filter show dev eth0 ingress

# 하드웨어 tc offload 기능 확인
ethtool -k eth0 | grep hw-tc-offload
# hw-tc-offload: on

# 하드웨어 tc offload 활성화
ethtool -K eth0 hw-tc-offload on
TCAM vs Hash 필터 선택: TCAM 필터는 와일드카드 매칭을 지원하지만 엔트리 수가 제한적(T6에서 2048개)입니다. 대량의 정확한 매칭 규칙이 필요한 경우 해시 기반 필터를 사용하면 수만 개의 규칙을 하드웨어에 오프로드할 수 있습니다. tc flower에서 skip_sw 플래그는 소프트웨어 경로를 건너뛰고 하드웨어만으로 필터링하며, skip_hw는 반대로 소프트웨어만 사용합니다.
cxgb4 디버깅: Chelsio 어댑터는 /sys/kernel/debug/cxgb4/ 디버그 파일시스템을 통해 내부 레지스터, 필터 테이블, 큐 상태, 펌웨어 로그 등을 상세히 확인할 수 있습니다. cudbg(Chelsio Unified Debug) 도구를 사용하면 어댑터 전체 상태를 덤프하여 오프라인 분석이 가능합니다.

Cloud NIC: ENA / gVNIC

Cloud NIC는 퍼블릭 클라우드 환경(AWS, GCP)에서 가상 머신에 노출되는 전용 네트워크 어댑터입니다. 기존 에뮬레이션 기반 NIC(e1000, rtl8139)와 달리, 호스트 하이퍼바이저의 네트워크 스택과 긴밀하게 통합되어 베어메탈에 가까운 성능을 제공합니다. AWS의 ENA(Elastic Network Adapter)와 Google Cloud의 gVNIC(Google Virtual NIC)이 대표적입니다.
Cloud NIC 드라이버 비교
항목ENA (AWS)gVNIC (GCP)
모듈명enagve
소스 위치drivers/net/ethernet/amazon/ena/drivers/net/ethernet/google/gve/
큐 인터페이스Admin Queue + I/O SQ/CQGQI / DQO (세대별)
최대 큐 수32 (인스턴스 타입별 상이)16 (인스턴스 타입별 상이)
최대 대역폭200 Gbps (p5.48xlarge)200 Gbps (C3D)
저지연 프로토콜ENA Express (SRD)해당 없음
XDP 지원O (v2.6.1+)O (GQI 모드)
라이선스GPL v2GPL v2

ENA (Elastic Network Adapter) 아키텍처

ENA는 AWS Nitro 하이퍼바이저와 통합된 네트워크 어댑터로, EC2 인스턴스에 고성능 네트워크 I/O를 제공합니다. ENA 디바이스는 Admin Queue를 통한 제어 경로와 I/O Submission/Completion Queue를 통한 데이터 경로를 분리하여 효율적인 패킷 처리를 달성합니다.

핵심 개념: ENA는 NVMe와 유사한 큐 기반 아키텍처를 채택합니다. Admin Queue는 NVMe의 Admin Queue에 해당하고, I/O SQ/CQ는 NVMe의 I/O Queue에 대응합니다. 이 설계 덕분에 멀티코어 환경에서 CPU별 독립 큐를 할당하여 lock 경합 없이 패킷을 처리할 수 있습니다.
아키텍처 개요

ENA 드라이버는 struct ena_adapter를 중심으로 동작합니다. 초기화 시 Admin Queue를 통해 디바이스와 협상하여 지원 기능, 큐 수, MTU 등을 결정합니다. 데이터 전송은 I/O SQ(Submission Queue)에 디스크립터를 기록하고 MMIO doorbell을 울려 디바이스에 통보하는 방식입니다. 완료된 패킷은 CQ(Completion Queue)를 통해 NAPI 폴링으로 처리됩니다.

EC2 Instance Application (socket API) 커널 네트워크 스택 ENA Driver (ena.ko) Admin Queue I/O SQ/CQ pairs MMIO Doorbells ENA Device (Nitro Card) Admin Engine I/O Processing Engine DMA Engine Network Port Network Fabric 제어 경로 데이터 경로 doorbell
그림 43. ENA 아키텍처 — Admin Queue 제어 경로와 I/O SQ/CQ 데이터 경로 분리
LLQ (Low Latency Queue) vs Regular Queue

ENA는 두 가지 큐 동작 모드를 지원합니다. Regular 모드에서는 디스크립터와 패킷 데이터가 모두 호스트 메모리에 존재하며, 디바이스가 DMA로 읽어갑니다. LLQ(Low Latency Queue) 모드에서는 디스크립터와 패킷 헤더를 디바이스 메모리(BAR 공간)에 직접 기록하여 DMA 왕복 지연을 제거합니다.

LLQ의 이점: 패킷 헤더(최대 224바이트)를 디바이스 메모리에 push 방식으로 기록하면, 디바이스가 호스트 메모리로 DMA 읽기를 수행하는 단계가 생략됩니다. 이로 인해 소형 패킷의 전송 지연이 수 마이크로초 단축됩니다. 대부분의 Nitro 기반 인스턴스에서 LLQ가 기본 활성화됩니다.
LLQ vs Regular Queue 비교 Regular Mode Host Memory Descriptor Pkt Data ENA Device (DMA 읽기) DMA ⏱ 높은 지연: doorbell → DMA desc → DMA data → 전송 1. Doorbell 2. DMA Desc 3. DMA Data 4. 전송 LLQ Mode (Low Latency Queue) Host Memory Pkt Body Device Memory Descriptor Pkt Header MMIO Push DMA Body ⏱ 낮은 지연: push desc+hdr → DMA body → 전송 1. Push Hdr 2. DMA Body 3. 전송
그림 44. LLQ vs Regular Queue — LLQ는 디스크립터+헤더를 디바이스 메모리에 직접 push하여 DMA 왕복 제거
ENA Express (SRD — Scalable Reliable Datagram)

ENA Express는 AWS가 개발한 SRD(Scalable Reliable Datagram) 프로토콜을 활용하여 단일 흐름(single flow)의 대역폭을 최대 25 Gbps까지 확장하고, p99 지연 시간을 크게 줄이는 기능입니다. 전통적인 TCP/UDP는 5-tuple 해싱으로 단일 경로에 바인딩되지만, SRD는 여러 물리 경로에 패킷을 분산하고 수신 측에서 재정렬합니다.

주의: ENA Express는 드라이버 수준이 아닌 Nitro 네트워크 인프라에서 동작합니다. 드라이버 코드에 SRD 로직이 포함되지 않으며, EC2 콘솔 또는 AWS CLI에서 인스턴스/ENI 단위로 활성화합니다. ENA Express가 활성화되면 ethtool -S에서 SRD 관련 통계를 확인할 수 있습니다.
인스턴스 타입별 ENA 기능 매트릭스
EC2 인스턴스 패밀리별 ENA 기능 지원
인스턴스 패밀리최대 대역폭LLQENA Express최대 큐 수Enhanced 모니터링
m5 / c5 / r525 GbpsOO8O
m6i / c6i / r6i50 GbpsOO16O
m7g / c7g / r7g30 GbpsOO16O
m7i / c7i50 GbpsOO32O
hpc7g200 GbpsOO32O
p5.48xlarge3200 Gbps (EFA)O-32O
t3 / t3a5 GbpsO-2O
큐 구성 및 인터럽트 조절

ENA 드라이버는 CPU 수와 디바이스가 보고하는 최대 큐 수 중 작은 값으로 큐를 생성합니다. 각 TX/RX 큐 쌍은 하나의 MSI-X 벡터에 매핑되며, NAPI 인스턴스가 할당됩니다. 적응형 인터럽트 조절(adaptive interrupt moderation)을 통해 처리량과 지연 시간 사이의 균형을 자동으로 조정합니다.

# ENA 큐 구성 확인
ethtool -l eth0
# Channel parameters for eth0:
# Pre-set maximums:
# RX:     0
# TX:     0
# Other:  0
# Combined:     8
# Current hardware settings:
# Combined:     4

# 큐 수 변경 (인스턴스 타입 최대값 이내)
ethtool -L eth0 combined 8

# 적응형 인터럽트 조절 상태 확인
ethtool -c eth0
# Adaptive RX: on  TX: on

# 고정 인터럽트 조절로 전환 (지연 최소화)
ethtool -C eth0 adaptive-rx off adaptive-tx off rx-usecs 20 tx-usecs 20

# 링 버퍼 크기 확인 및 조정
ethtool -g eth0
ethtool -G eth0 rx 8192 tx 8192
ENA 메트릭 및 모니터링

ENA 드라이버는 풍부한 통계 카운터를 제공합니다. ethtool -S로 큐별 패킷/바이트 카운터, 오류 카운터, LLQ 통계, SRD 통계를 확인할 수 있습니다.

# ENA 전체 통계 확인
ethtool -S eth0

# 주요 카운터 필터링
ethtool -S eth0 | grep -E "(tx_bytes|rx_bytes|tx_pkts|rx_pkts)"

# SRD(ENA Express) 통계 확인
ethtool -S eth0 | grep -i srd
# ena_srd_mode: 1
# ena_srd_tx_pkts: 1234567
# ena_srd_eligible_tx_pkts: 2345678
# ena_srd_rx_pkts: 1234000

# ENA 드라이버 버전 확인
ethtool -i eth0
# driver: ena
# version: 2.10.0g
# firmware-version: ...

# dmesg에서 ENA 초기화 로그 확인
dmesg | grep -i ena
# ena 0000:00:05.0: ENA device registered
# ena 0000:00:05.0: LLQ is enabled
# ena 0000:00:05.0: Creating 8 io queues
성능 튜닝 팁: ENA의 최적 성능을 위해서는 (1) RPS/XPS를 적절히 설정하여 CPU 분산을 최적화하고, (2) 점보 프레임(MTU 9001)을 활성화하며, (3) TCP 세그먼트 수를 늘리고(net.ipv4.tcp_max_syn_backlog), (4) 적응형 인터럽트 조절을 워크로드에 맞게 튜닝하는 것이 좋습니다.
# ENA 기능 확인 (offload 지원)
ethtool -k eth0 | grep -E "(tx-checksumming|rx-checksumming|scatter-gather|tso|gro|lro)"
# tx-checksumming: on
# rx-checksumming: on
# scatter-gather: on
# tcp-segmentation-offload: on
# generic-receive-offload: on

# XDP 프로그램 로드
ip link set dev eth0 xdpdrv obj xdp_prog.o sec xdp

# XDP 통계 확인
ethtool -S eth0 | grep xdp
# rx_queue_0_xdp_aborted: 0
# rx_queue_0_xdp_drop: 0
# rx_queue_0_xdp_pass: 1234567
# rx_queue_0_xdp_tx: 0
# rx_queue_0_xdp_redirect: 0
ENA Express와 SRD(Scalable Reliable Datagram) 심화

ENA Express는 AWS Nitro 인프라 수준에서 동작하는 SRD(Scalable Reliable Datagram) 프로토콜을 활용합니다. 기존 TCP/UDP는 5-tuple 해싱으로 단일 물리 경로에 고정되지만, SRD는 패킷을 여러 경로에 per-packet spraying 방식으로 분산하여 단일 흐름의 대역폭 상한을 물리 경로 수에 비례해서 높입니다.

Per-packet spraying vs ECMP per-flow hashing: 전통적인 ECMP는 flow 단위로 경로를 결정하므로 단일 TCP 흐름은 하나의 링크로만 전송됩니다. 링크 용량(예: 5 Gbps)이 단일 흐름의 상한이 됩니다. SRD는 개별 패킷을 서로 다른 경로로 분산하므로, 4개 경로가 있을 때 단일 흐름이 이론적으로 4× 대역폭을 사용할 수 있습니다. 실측에서는 5 Gbps 상한이 25 Gbps까지 확장됩니다.

수신 측 재정렬(Receiver-side reordering): 패킷을 여러 경로로 보내면 도착 순서가 뒤섞입니다. SRD는 Nitro Card 수신 측에서 투명하게 재정렬하여 TCP 스택에는 순서가 보장된 스트림이 전달됩니다. TCP 스택이 재정렬을 처리할 필요가 없어 불필요한 재전송/SACK 오버헤드가 발생하지 않습니다.

SRD(Scalable Reliable Datagram) 멀티패스 아키텍처 Per-packet spraying → Nitro Card 수신 측 재정렬 → TCP 투명 전달 Instance A ENA (Nitro Card) SRD per-packet spraying Nitro Card A (sender) Pkt sprayer Seq numbering Path selector 경로 1 (Pkt 1,5…) 경로 2 (Pkt 2,6…) 경로 3 (Pkt 3,7…) 경로 4 (Pkt 4,8…) Path 1 Path 2 Path 3 Path 4 Nitro Card B (receiver) Reorder buffer Seq reordering TCP-transparent out Instance B TCP stack sees ordered stream only 기존 ECMP: ~5 Gbps/flow SRD 멀티패스: ~25 Gbps/flow p99 지연 ~50% 감소 (경로 혼잡 우회)
그림 68. SRD 멀티패스 아키텍처 — 패킷을 4개 경로에 분산하고 수신 측 Nitro Card에서 재정렬하여 단일 흐름 25 Gbps 달성

SRD 비활성화 조건: 다음 상황에서는 SRD가 자동으로 비활성화됩니다. (1) Cross-AZ 트래픽: AZ를 넘어가는 경로에서는 멀티패스 이점이 없으며 재정렬 지연이 발생합니다. (2) 미지원 인스턴스 타입: t3, t3a, c1, m1 등 구세대 인스턴스는 Nitro 기반이 아닙니다. (3) 비 TCP/UDP 트래픽: ICMP, GRE, IPsec 등 다른 프로토콜은 SRD 경로를 사용하지 않습니다. (4) 상대방 미지원: 목적지 인스턴스가 ENA Express를 지원하지 않는 경우.

성능 수치: AWS 공식 벤치마크에서 단일 TCP 흐름 기준으로 최대 대역폭이 5 Gbps → 25 Gbps로 개선되었습니다. p99 지연 시간은 기존 대비 약 50% 감소하며, 이는 경로 혼잡이 발생해도 다른 경로로 즉각 우회하기 때문입니다.

SRD 모니터링: ethtool -Sena_srd_eligible_tx_pktsena_srd_tx_pkts 비율로 SRD 활용도를 측정합니다. 비율이 낮으면 cross-AZ 트래픽이 많거나 대상 인스턴스가 ENA Express를 지원하지 않는 것입니다.

# SRD 통계 상세 확인
ethtool -S eth0 | grep -i srd
# ena_srd_mode: 1                    ← 1=활성, 0=비활성
# ena_srd_tx_pkts: 1234567           ← 실제 SRD 경로로 전송된 패킷
# ena_srd_eligible_tx_pkts: 2345678  ← SRD 적용 가능했던 총 패킷
# ena_srd_rx_pkts: 1234000           ← SRD 경로로 수신된 패킷
# ena_srd_resource_utilization: 7800 ← SRD 리소스 활용도 (0-10000)

# SRD 활용률 계산 (bash)
SRD_TX=$(ethtool -S eth0 | awk '/ena_srd_tx_pkts:/ {print $2}')
SRD_ELIG=$(ethtool -S eth0 | awk '/ena_srd_eligible_tx_pkts:/ {print $2}')
echo "SRD 활용률: $(( SRD_TX * 100 / SRD_ELIG ))%"

# ENA Express 활성화 (AWS CLI)
aws ec2 modify-network-interface-attribute \
    --network-interface-id eni-xxxxxxxxxxxxxxxxx \
    --ena-srd-specification \
    '{"EnaSrdEnabled":true,"EnaSrdUdpSpecification":{"EnaSrdUdpEnabled":true}}'

# ENA Express 상태 확인
aws ec2 describe-network-interface-attribute \
    --network-interface-id eni-xxxxxxxxxxxxxxxxx \
    --attribute enaSrdSpecification
ENA 장애 감지 및 복구

ENA 드라이버는 Nitro 인프라와의 통신 장애를 자동으로 감지하고 복구합니다. 핵심은 AdminQ timeout 감지keep-alive watchdog의 두 가지 메커니즘입니다.

Keep-alive watchdog: ena_timer_service()가 주기적으로(기본 1초 간격) 실행되며, 디바이스로부터 keep-alive 응답이 6초(6회 타임아웃) 내에 오지 않으면 장애로 판단합니다. watchdog은 AdminQ를 통해 keep-alive 패킷을 보내고, Nitro Card가 응답하는 방식입니다. keep-alive 응답 실패는 Nitro Card 재시작 또는 네트워크 인프라 점검 중에 발생할 수 있습니다.

장치 리셋 흐름: 장애가 감지되면 드라이버는 ena_com_dev_reset()을 호출하여 순서대로 (1) 모든 TX/RX 큐 중단, (2) outstanding DMA 완료 대기, (3) AdminQ 리셋 명령 전송, (4) 큐 해제, (5) 디바이스 재초기화, (6) 큐 재구성을 수행합니다. 이 과정에서 커널 네트워크 스택은 링크 다운(carrier off)을 감지하여 상위 레이어에 알립니다.

큐 불일치 복구(Queue mismatch recovery): 드라이버가 리셋 후 재초기화할 때 디바이스가 이전과 다른 수의 큐를 보고하는 경우(예: Nitro 업데이트 중)가 있습니다. 드라이버는 새로 보고된 큐 수로 재구성하고, RPS/XPS 설정도 함께 재적용합니다.

프로덕션 트래픽 중 ENA 리셋 영향: ENA 리셋은 일반적으로 1~3초의 네트워크 중단을 유발합니다. TCP 세션은 재전송 타이머(RTO)에 의해 자동 복구되지만, 짧은 RTO 설정 (net.ipv4.tcp_retries2 감소)은 세션이 리셋 전에 끊길 수 있습니다. 완화 방법: (1) multi-path 애플리케이션의 경우 연결 재시도 로직 구현, (2) ENA Enhanced Networking 활성화로 리셋 빈도 최소화, (3) CloudWatch NetworkPacketsOut 지표에 알람 설정.
ENA 주요 장애 패턴 및 dmesg 메시지
장애 유형dmesg 패턴원인대응
AdminQ 타임아웃ena: admin_q timed outNitro Card 과부하, 인프라 점검자동 리셋; 반복 시 인스턴스 교체
Keep-alive 실패ena: Keep alive watchdog timeout호스트 인프라 재시작자동 복구; 반복 시 AWS Support 연락
TX 타임아웃ena: TX timeout on queue X큐 스톨, HW 버그drv 버전 업그레이드 확인
불량 완료 디스크립터ena: Invalid req_idDMA 오염, 드라이버 버그커널/드라이버 업데이트
메모리 할당 실패ena: Failed to alloc호스트 메모리 부족인스턴스 타입 확장
# bpftrace로 AdminQ 지연 추적 (1ms 초과 이벤트 출력)
bpftrace -e '
kprobe:ena_com_wait_for_abort_completion {
    @start[tid] = nsecs;
}
kretprobe:ena_com_wait_for_abort_completion {
    $lat = nsecs - @start[tid];
    if ($lat > 1000000) {
        printf("AdminQ latency: %d us (pid %d)\n", $lat/1000, pid);
    }
    delete(@start[tid]);
}'

# ENA 리셋 이벤트 실시간 모니터링
dmesg -w | grep -E "(ena.*reset|ena.*timeout|ena.*watchdog)"

# ENA 드라이버 통계에서 리셋/오류 카운터 확인
ethtool -S eth0 | grep -E "(reset|watchdog|admin)"
# dev_stats_reset_fail: 0
# dev_stats_tx_timeout: 0
# ena_admin_q_pause: 0

# 최근 1시간 이내 ENA 관련 커널 메시지 확인
journalctl -k --since "1 hour ago" | grep -i "ena"

gVNIC (Google Virtual NIC) 아키텍처

gVNIC은 Google Cloud의 차세대 가상 네트워크 어댑터입니다. 기존의 VirtIO 기반 NIC를 대체하여 높은 대역폭과 낮은 지연 시간을 제공합니다. gVNIC은 두 가지 큐 인터페이스를 지원합니다: 기존의 GQI(Google Queue Interface)와 차세대 DQO(Descriptor Queue Organization)입니다.

GQI vs DQO: GQI는 1세대 인터페이스로 공유 메모리 기반 디스크립터 링을 사용합니다. DQO는 2세대 인터페이스로 TX/RX 각각에 디스크립터 큐와 별도의 Completion 큐를 두어, NVMe와 유사한 방식으로 더 효율적인 완료 처리를 지원합니다. C3/C3D/H3 등 최신 인스턴스에서는 DQO가 기본으로 사용됩니다.
GQI (Google Queue Interface) 모델 Guest VM gve Driver (GQI mode) TX Desc Ring (QPL buffer) RX Desc Ring (QPL buffer) Shared Memory (Queue Page List) Guest ↔ Host 공유 페이지 영역 Notification Blocks (doorbell) Host (gVNIC Backend) gVNIC Backend Engine Shared Memory Access QPL 페이지를 직접 참조 (복사 불필요) Andromeda vSwitch Physical NIC QPL 공유 notify Network
그림 45. GQI 모델 — QPL(Queue Page List) 공유 메모리 기반 디스크립터 링
DQO (Descriptor Queue Organization) 모델 Guest VM gve Driver (DQO mode) TX Desc Queue TX Compl Queue RX Desc Queue RX Compl Queue DMA-mapped buffers (per-packet) Host (gVNIC Backend) DMA Engine Packet Processing Pipeline Completion Generation Andromeda → Network DMA fetch completion
그림 46. DQO 모델 — 디스크립터 큐와 Completion 큐를 분리하여 NVMe 스타일의 비동기 완료 처리
GQI vs DQO 성능 특성 비교 상대 성능 (%) 0 25 50 75 100 처리량 (단일 흐름) 처리량 (다중 흐름) 지연 시간 (p50) 지연 시간 (p99) CPU 효율 GQI DQO 75% 100% 90% 97% 75% 92% 62% 87% 70% 90%
그림 47. GQI vs DQO 성능 비교 — DQO가 모든 지표에서 GQI 대비 우수 (높을수록 좋음)
GQI vs DQO 기능 비교
GQI vs DQO 상세 비교
항목GQIDQO
디스크립터 형식통합 디스크립터 링분리된 Desc/Compl 큐
메모리 모델QPL (Queue Page List) 공유 메모리per-packet DMA 매핑
완료 처리같은 링에서 완료 확인별도 Completion Queue
버퍼 관리사전 할당된 공유 페이지 풀동적 DMA 버퍼 할당
XDP 지원OO (5.18+)
Header SplitXO
큐당 최대 디스크립터10244096
인터럽트 조절기본적응형(Dim) 지원
지원 인스턴스N1, N2, E2, T2DC3, C3D, H3, A3, Z3
최대 대역폭100 Gbps200 Gbps
인스턴스 타입별 큐 모델 매핑
GCP 인스턴스와 gVNIC 큐 모델
인스턴스 시리즈큐 모델최대 NIC 수최대 큐 수최대 대역폭
N1 / N2 / N2DGQI81632 Gbps (100G Tier1)
E2GQI8816 Gbps
T2DGQI8832 Gbps
C3 / C3DDQO1516200 Gbps (C3D)
H3DQO816200 Gbps
A3DQO1516200 Gbps
Z3DQO1516100 Gbps
gVNIC 설정 및 튜닝
# gVNIC 드라이버 정보 확인
ethtool -i eth0
# driver: gve
# version: 1.4.0-K
# firmware-version: GVE-PROD-1.4.0

# 큐 모델 확인 (dmesg)
dmesg | grep gve
# gve 0000:00:04.0: GVE version: 1.4.0
# gve 0000:00:04.0: Queue format: DQO

# 큐 수 확인 및 변경
ethtool -l eth0
ethtool -L eth0 combined 16

# 링 버퍼 크기 조정
ethtool -G eth0 rx 4096 tx 4096

# offload 기능 확인
ethtool -k eth0 | grep -E "(checksum|segmentation|offload|gro)"

# 통계 확인 (큐별)
ethtool -S eth0 | head -40
# tx_queue_0_packets: 123456
# tx_queue_0_bytes: 78901234
# rx_queue_0_packets: 234567
# rx_queue_0_bytes: 89012345

# RPS 설정 (DQO 모드에서도 유용)
for i in /sys/class/net/eth0/queues/rx-*/rps_cpus; do
    echo "ff" > "$i"
done

# XPS 설정
for i in $(seq 0 7); do
    echo "$(( 1 << i ))" > /sys/class/net/eth0/queues/tx-$i/xps_cpus
done
GCP 네트워크 성능 최적화: (1) Tier_1 네트워킹을 활성화하면 N2 인스턴스에서도 최대 100 Gbps 대역폭을 사용할 수 있습니다. (2) gVNIC은 Jumbo Frame(MTU 8896)을 지원하며, VPC 내부 통신에서 대역폭을 크게 향상시킵니다. (3) gcloud compute instances create--network-interface=nic-type=GVNIC을 명시해야 합니다.
DQO Header Split 심화

DQO 모드의 핵심 최적화 중 하나는 Header Split입니다. 수신 패킷의 헤더와 페이로드를 서로 다른 메모리 풀에 배치하여 CPU 캐시 효율을 극대화합니다. 헤더는 L1/L2 캐시에 들어오는 작은 버퍼 풀에, 페이로드는 L3/LLC에 남아있는 큰 페이지 풀에 할당됩니다.

캐시 최적화 원리: 네트워크 스택은 패킷 처리 초기에 헤더(IP, TCP 등 ~80바이트)만 접근합니다. Header Split 없이 전체 패킷(헤더+페이로드)이 한 버퍼에 있으면, 페이로드 데이터가 L1/L2 캐시를 오염시켜 헤더 접근 지연이 증가합니다. 분리하면 헤더만 캐시에 올라와 처리 속도가 향상됩니다.

메모리 레이아웃: 헤더 버퍼 풀은 256B 크기의 작은 버퍼를 사전 할당하여 관리합니다. 데이터 버퍼 풀은 2K~4K 페이지 크기로 page_pool을 통해 관리됩니다. 소형 패킷 워크로드에서 memcpy 감소와 캐시 지역성 향상으로 약 15%의 처리 성능 향상이 측정됩니다.

QPL vs DQO 메모리 모델

QPL(Queue Page List)은 GQI 모드에서 사용하는 메모리 모델로, GCE 하이퍼바이저의 DMA 제약을 우회하기 위해 도입되었습니다. 게스트 VM이 특정 메모리 페이지를 하이퍼바이저와 공유 등록하여 bounce buffer로 사용합니다. 이 구조는 호스트-게스트 간 DMA 직접 매핑이 어려운 환경에서 안정적이지만 메모리 오버헤드가 큽니다.

QPL 메모리 크기 계산: QPL이 사용하는 메모리는 ring_size × max_packet_size로 계산됩니다. 예를 들어 ring_size=1024이고 max_packet_size=9000(jumbo frame)인 경우 큐당 약 9 MB, 16개 TX/RX 큐 쌍이면 총 ~288 MB가 QPL로 예약됩니다.

DQO가 QPL을 제거한 이유: DQO는 page_pool 기반의 직접 DMA 매핑을 사용합니다. 하이퍼바이저가 DQO를 지원하면 bounce buffer 없이 게스트 메모리에 직접 DMA 쓰기가 가능합니다. 이로 인해 QPL의 메모리 낭비가 사라지고, 불필요한 데이터 복사도 제거됩니다.

DQO Header Split 버퍼 풀 구조 gVNIC (DQO mode) RX Desc Q RX Compl Q DMA Engine Header + Data Header Pool (256B 고정 크기) hdr[0] hdr[1] L1/L2 캐시 적합 Data Pool (2K~4K 페이지) page[0] page[1] page_pool 관리 DMA hdr DMA data GRO merge skb coalesce frag list merge Network Stack TCP/IP processing hdr: L1/L2 cache hit data: zero-copy possible ~15% 성능 향상 ← 헤더: L1/L2 적합 (소형) | 데이터: L3/LLC 수용 (대형) →
그림 69. DQO Header Split — 헤더(256B)와 페이로드(4K page)를 별도 풀에 분리하여 캐시 효율 최적화
# gVNIC 큐 모드 확인 (DQO vs GQI)
dmesg | grep -i "queue format\|gve.*dqo\|gve.*gqi"
# gve 0000:00:04.0: Queue format: DQO  ← DQO 모드
# gve 0000:00:04.0: Queue format: GQI  ← GQI 모드

# Header Split 지원 여부 확인
ethtool -k eth0 | grep "header-split"
# rx-header-split: on [fixed]  ← DQO 모드에서 활성

# QPL 메모리 사용량 확인 (GQI 모드)
ethtool -S eth0 | grep qpl
# tx_qpl_allocated: 16   ← TX QPL 페이지 수
# rx_qpl_allocated: 16   ← RX QPL 페이지 수

# DQO page_pool 통계 확인
ethtool -S eth0 | grep -E "(rx_buf|page_pool|alloc)"
# rx_buf_alloc_fail: 0   ← 버퍼 할당 실패 없음

# 메모리 사용 비교 (QPL vs DQO)
# QPL 예시: ring=1024, mtu=9000 → 1024*9000*2(TX+RX)=~18MB/큐
# DQO: page_pool 동적 할당, 사용 중인 패킷만 메모리 점유
클라우드 NIC 장애 복구 비교

세 가지 주요 클라우드 NIC(ENA, gVNIC, hv_netvsc)는 서로 다른 장애 감지 및 복구 메커니즘을 가집니다. 실제 운영 환경에서 장애 대응 전략을 수립할 때 각 NIC의 특성을 이해하는 것이 중요합니다.

클라우드 NIC 드라이버 장애 복구 비교
항목ENA (AWS)gVNIC (GCP)hv_netvsc (Azure)
장애 감지 방식 AdminQ keep-alive watchdog Admin queue + notify block polling VMBus 채널 heartbeat
Keep-alive 주기 1초 간격, 6초 타임아웃 2초 간격, 10초 타임아웃 VMBus 자체 heartbeat (~5초)
복구 메커니즘 ena_com_dev_reset() 호출 gve_reset() workqueue netvsc_channel_cb() 재연결
큐 재구성 전체 큐 해제 후 재생성 큐 tear-down + re-register VMBus 채널 재열기 + SR-IOV VF 재연결
다운타임 (일반) 1~3초 2~5초 1~4초 (SR-IOV 시 <1초)
VF failover 해당 없음 (EFA 별도) 해당 없음 SR-IOV VF → netvsc synthetic 자동 failover
모니터링 커맨드 ethtool -S eth0 | grep ena_admin ethtool -S eth0 | grep tx_timeout ethtool -S eth0 | grep ring_full
클라우드 NIC 헬스 모니터링 권장 사항: (1) 공통: ethtool -S의 오류 카운터를 1분 주기로 수집하여 이상 증가 시 알람 설정. (2) ENA: CloudWatch NetworkPacketsOut=0 조건 알람으로 NIC 완전 중단 감지. (3) gVNIC: GCP 인스턴스의 compute.googleapis.com/instance/network/received_packets_count 지표 모니터링으로 재시작 패턴 식별. (4) hv_netvsc: Azure Monitor의 Network In/Out 지표와 dmesghv_netvsc 메시지를 조합하여 VF failover 횟수 추적.

가상화 NIC: virtio_net / vmxnet3 / hv_netvsc

가상화 NIC는 하이퍼바이저가 게스트 가상 머신에 제공하는 반가상화(paravirtualized) 네트워크 어댑터입니다. 하드웨어를 완전 에뮬레이션하는 방식(e1000, rtl8139)과 달리, 게스트와 호스트가 협력하여 최적화된 데이터 경로를 구성합니다. virtio_net(KVM/QEMU), vmxnet3(VMware), hv_netvsc(Hyper-V/Azure)가 각 하이퍼바이저 생태계의 표준 반가상화 NIC입니다.
가상화 NIC 드라이버 비교
항목virtio_netvmxnet3hv_netvsc
하이퍼바이저KVM / QEMUVMware ESXiHyper-V / Azure
모듈명virtio_netvmxnet3hv_netvsc
소스 위치drivers/net/virtio_net.cdrivers/net/vmxnet3/drivers/net/hyperv/
전송 인터페이스virtqueue (vring)공유 메모리 큐VMBus + VF
최대 큐 수호스트 설정 의존32 (vHW v4+)64 (RSS)
SR-IOV VF 통합X (별도 구성)UPT passthroughO (Accelerated Networking)
XDP 지원O (5.0+)XO (5.6+)
라이선스GPL v2GPL v2GPL v2

virtio_net 심화

virtio_net은 OASIS virtio 표준(1.0/1.1/1.2)에 기반한 반가상화 네트워크 드라이버입니다. 게스트 커널의 virtio_net 드라이버와 호스트의 백엔드(QEMU, vhost-net, vhost-user) 사이에 virtqueue라는 공유 링 버퍼를 통해 패킷을 교환합니다. 이 구조는 불필요한 VM exit를 최소화하고, 배치 처리와 인터럽트 합산을 통해 높은 처리량을 달성합니다.

virtqueue 링 구조

각 virtqueue는 세 개의 영역으로 구성됩니다: Descriptor Table(버퍼 주소/크기/플래그), Available Ring(게스트→호스트), Used Ring(호스트→게스트). 게스트가 패킷을 전송하려면 Descriptor Table에 버퍼를 등록하고 Available Ring에 인덱스를 추가한 후 호스트에 통보(kick)합니다. 호스트가 처리를 완료하면 Used Ring에 인덱스를 기록하고 인터럽트를 발생시킵니다.

Packed Virtqueue (virtio 1.1): 기존 Split Virtqueue에서는 Descriptor Table, Available Ring, Used Ring이 별도 메모리 영역에 존재하여 캐시 효율이 낮았습니다. virtio 1.1에서 도입된 Packed Virtqueue는 세 영역을 하나의 링으로 통합하여 캐시 라인 활용도를 높이고, wrap counter 기반으로 소유권을 관리합니다.
Guest VM virtio_net Driver TX Virtqueue desc + avail + used RX Virtqueue desc + avail + used vring (Shared Memory) Guest-Host 공유 메모리 영역 NAPI poll Mergeable Bufs Control Virtqueue (CVQ) kick (notify) interrupt Host vhost-net (kernel module) QEMU (userspace 폴백) vhost-user (DPDK 등) tap device Linux Bridge OVS Physical NIC Network vring 접근
그림 48. virtio_net 아키텍처 — Guest의 virtqueue에서 Host의 vhost-net/QEMU를 거쳐 물리 NIC까지의 전체 데이터 경로
Virtqueue 디스크립터 체인과 링 동작 Descriptor Table [0] addr=0x1000 len=14 F=NEXT [1] addr=0x2000 len=1500 F=0 [2] addr=0x3000 len=4096 F=WR [3] addr=0x4000 len=4096 F=WR [4] (free) ... next Available Ring (Guest → Host) idx=0 idx=2 ... flags | idx | ring[] | used_event Used Ring (Host → Guest) id=0,len=14 ... flags | idx | ring[]{id,len} Host Backend 1. avail ring 확인 2. desc chain 처리 3. used ring 기록 4. interrupt 발생 kick 완료
그림 49. Virtqueue 디스크립터 체인 — Descriptor Table에서 체인을 구성하고 Available/Used Ring으로 소유권 이전
vhost-net (커널) vs vhost-user (유저스페이스) 비교 vhost-net (커널 모드) Guest VM virtio_net driver vring (공유 메모리) QEMU (제어만) vhost-net 커널 모듈 커널 스레드가 vring 직접 접근 (제로 카피) tap device Physical NIC 직접 접근 장점 VM exit 최소, 제로 카피 vhost-user (유저스페이스 모드) Guest VM virtio_net driver vring (공유 메모리) QEMU (제어만) vhost-user Backend (DPDK/SPDK) 유저 프로세스가 hugepage 통해 vring 접근 mmap 접근 Physical NIC (PMD 직접) 장점 커널 우회, 폴링 모드, 극저지연 Unix Socket 제어 (fd 전달, 메모리 맵)
그림 50. vhost-net vs vhost-user — vhost-net은 커널에서 vring 직접 처리, vhost-user는 DPDK 등 유저스페이스에서 hugepage 기반 접근
Feature Negotiation
virtio_net 주요 Feature Bit
Feature Bit이름설명성능 영향
0VIRTIO_NET_F_CSUM호스트가 체크섬 오프로드 지원TX CPU 부하 감소
1VIRTIO_NET_F_GUEST_CSUM게스트가 체크섬 오프로드 처리RX CPU 부하 감소
5VIRTIO_NET_F_MAC디바이스가 MAC 주소 제공-
11VIRTIO_NET_F_HOST_TSO4호스트가 TCP Segmentation Offload 지원대역폭 증가
15VIRTIO_NET_F_MRG_RXBUFMergeable RX Buffers대형 패킷 효율 향상
17VIRTIO_NET_F_STATUS링크 상태 알림-
18VIRTIO_NET_F_CTRL_VQControl Virtqueue 지원동적 설정 변경
22VIRTIO_NET_F_MQ멀티큐 지원멀티코어 스케일링
25VIRTIO_NET_F_SPEED_DUPLEX속도/이중 모드 설정-
34VIRTIO_F_ORDER_PLATFORM플랫폼 메모리 순서 사용배리어 오버헤드 감소
39VIRTIO_F_RING_PACKEDPacked Virtqueue (1.1)캐시 효율 향상
Mergeable Buffers와 멀티큐 구성

Mergeable Buffers(VIRTIO_NET_F_MRG_RXBUF)는 수신 패킷이 단일 버퍼에 맞지 않을 때 여러 디스크립터의 버퍼를 합쳐서 하나의 패킷으로 조립하는 기능입니다. 점보 프레임이나 GRO에 의해 합쳐진 대형 패킷 처리에 필수적입니다. mergeable이 비활성화되면 각 디스크립터가 MTU 크기 버퍼를 가져야 하므로 메모리 낭비가 발생합니다.

# virtio_net feature 확인
cat /sys/bus/virtio/devices/virtio0/features
# 또는 ethtool로 확인
ethtool -i eth0
# driver: virtio_net

# 멀티큐 설정 (QEMU 측: -device virtio-net-pci,mq=on,vectors=10)
# 게스트에서 큐 수 확인
ethtool -l eth0
# Pre-set maximums:
# Combined:     8
# Current hardware settings:
# Combined:     4

# 멀티큐 활성화
ethtool -L eth0 combined 8

# 큐별 통계 확인
ethtool -S eth0 | grep -E "^(tx|rx)_queue"

# IRQ affinity 확인
grep virtio /proc/interrupts

# mergeable buffer 동작 확인
ethtool -k eth0 | grep gro
# generic-receive-offload: on
XDP 지원

virtio_net은 Linux 5.0부터 XDP를 지원합니다. XDP_TX, XDP_REDIRECT, XDP_DROP, XDP_PASS 모든 액션이 지원되며, XDP_TX의 경우 전용 SQ를 할당하여 일반 TX 경로와 간섭을 방지합니다. vhost-net 백엔드 사용 시 XDP와 함께 제로 카피 수신이 가능하여 높은 성능을 달성합니다.

# XDP 프로그램 로드 (native mode)
ip link set dev eth0 xdpdrv obj xdp_drop.o sec xdp

# XDP 프로그램 확인
ip link show eth0
# ... xdp/id:42 ...

# XDP 통계 확인
ethtool -S eth0 | grep xdp
# rx_queue_0_xdp_packets: 123456
# rx_queue_0_xdp_drops: 0
# rx_queue_0_xdp_redirects: 0

# XDP 프로그램 제거
ip link set dev eth0 xdp off
Guest-Host 최적화 팁
virtio_net 성능 최적화 체크리스트:
  • vhost-net 활성화: QEMU의 -netdev tap,vhost=on으로 커널 vhost 사용
  • 멀티큐: vCPU 수에 맞춰 큐 수 설정 (mq=on,queues=N)
  • Mergeable buffers: 점보 프레임 사용 시 필수 활성화
  • Packed Virtqueue: QEMU 6.0+ / 커널 5.0+에서 packed=on
  • Hugepages: 게스트 메모리에 hugepage 할당으로 TLB miss 감소
  • vIOMMU 비활성화: 불필요한 IOMMU 오버헤드 제거
  • CPU pinning: vCPU를 물리 CPU에 고정하여 캐시 효율 향상
Packed Virtqueue 심화

virtio 1.1에서 도입된 Packed Virtqueue는 기존 Split Virtqueue의 메모리 레이아웃을 근본적으로 재설계했다.

항목Split VirtqueuePacked Virtqueue
메모리 영역3개 분리 (Desc Table + Avail Ring + Used Ring)단일 Descriptor 배열
소유권 표시별도 인덱스 카운터wrap counter 비트 (AVAIL/USED 플래그)
캐시 라인 접근여러 메모리 영역 순회 → 캐시 미스 높음단일 배열 순차 접근 → 캐시 미스 ~30% 감소
Feature bit기본 (협상 불필요)VIRTIO_F_RING_PACKED 협상 필요
지원 커널모든 virtio 커널Linux 5.0+, QEMU 4.2+

Packed Virtqueue에서 wrap counter는 드라이버와 디바이스가 링을 한 바퀴 돌 때마다 0↔1로 토글되는 1비트 카운터다. 디스크립터의 AVAIL/USED 플래그 조합이 현재 wrap counter 값과 일치할 때 해당 디스크립터를 처리 가능 상태로 인식한다. 이 방식은 별도의 Available Ring과 Used Ring 없이도 소유권을 추적할 수 있어 포인터 체이싱 없는 선형 메모리 접근이 가능하다.

# Packed Virtqueue 활성화 확인 (QEMU 6.0+ 게스트)
cat /sys/bus/virtio/devices/virtio0/features | tr ' ' '\n' | grep -c "39"
# 1이면 VIRTIO_F_RING_PACKED 협상 성공

# QEMU 명령줄에서 Packed Virtqueue 활성화
# -device virtio-net-pci,packed=on,mq=on,vectors=10

# 게스트에서 virtio 기능 비트 확인
cat /sys/bus/virtio/devices/virtio0/features
vDPA(virtio Data Path Acceleration)

vDPA는 하드웨어가 virtio 데이터 경로를 직접 구현하여 QEMU 유저스페이스 처리 오버헤드를 완전히 제거하는 아키텍처다. mlx5_vdpa 드라이버는 ConnectX-6 이상에서 하드웨어 virtio 데이터 경로를 제공한다.

항목virtio-net (QEMU)vhost-net (커널)vDPA (mlx5)
데이터 경로QEMU 유저스페이스커널 vhost 스레드NIC 하드웨어 직접
VM exit 횟수많음적음최소 (거의 없음)
처리량 (100G 기준)~40 Gbps~70 Gbps~95 Gbps
CPU 오버헤드높음 (userspace 처리)중간 (kernel thread)낮음 (HW 처리)
라이브 마이그레이션지원지원제한적 (FW 지원 필요)
관리 인터페이스QEMU QMPioctl(vhost)vDPA bus + devlink

vDPA 아키텍처는 세 계층으로 구성된다: vDPA bus(커널 버스 추상화), vDPA device(mlx5_vdpa 등 하드웨어 드라이버), mediator driver(virtio-vdpa 또는 vhost-vdpa). mediator가 virtio 프로토콜 변환을 담당하므로 게스트 드라이버 수정 없이 기존 virtio_net 드라이버와 완전히 호환된다.

vmxnet3 심화

vmxnet3는 VMware가 설계한 3세대 반가상화 네트워크 어댑터입니다. ESXi 하이퍼바이저의 vmkernel과 긴밀하게 통합되어, 에뮬레이션 오버헤드 없이 고성능 네트워크 I/O를 제공합니다. vmxnet3는 공유 메모리 기반의 TX/RX 큐, RSS(Receive Side Scaling), LRO(Large Receive Offload), 적응형 인터럽트 합산을 지원합니다.

UPT (Uniform Passthrough) 모드: vmxnet3는 SR-IOV VF를 투명하게 통합하는 UPT 모드를 지원합니다. UPT 활성 시 패킷이 vmkernel을 우회하여 VF에서 게스트로 직접 전달되며, VF 하드웨어가 없거나 기능 제한이 필요한 경우 자동으로 에뮬레이션 모드로 폴백합니다. 게스트 드라이버 관점에서 경로 전환은 투명합니다.
Guest VM vmxnet3 Driver TX Queue desc + data ring RX Queue ring1 + ring2 Completion Rings (TX/RX) Shared Memory Region driver ↔ vmkernel 공유 RSS (Toeplitz) Interrupt Coalescing ESXi vmkernel vmxnet3 Backend vSwitch (Standard/DVS) Uplink vmnic 에뮬레이션 Physical NIC UPT Bypass SR-IOV VF Direct I/O Physical NIC UPT (vmkernel 우회)
그림 51. vmxnet3 아키텍처 — 에뮬레이션 경로(vmkernel 경유)와 UPT bypass 경로(SR-IOV VF 직접)
vHW 버전별 기능 매트릭스
vmxnet3 Virtual Hardware 버전별 지원 기능
기능vHW v1vHW v2vHW v3vHW v4vHW v5vHW v6vHW v7
ESXi 최소 버전5.56.06.56.77.07.0 U28.0
최대 TX/RX 큐8/88/88/832/3232/3232/3232/32
RSSOOOOOOO
LROOOOOOOO
Coalescing 세분화기본기본확장확장확장확장확장
UPT (SR-IOV)XOOOOOO
TimestampingXXXXOOO
Uniform Passthrough v2XXXXXXO
Large TX descXXXOOOO
vmxnet3 설정 및 튜닝
# vmxnet3 드라이버 정보 확인
ethtool -i eth0
# driver: vmxnet3
# version: 1.7.0.0-k
# firmware-version: ...

# 큐 설정 확인
ethtool -l eth0
# Combined:     8

# Coalescing 설정 확인 및 조정
ethtool -c eth0
# rx-usecs: 50
# rx-frames: 64

# 낮은 지연 시간 모드
ethtool -C eth0 rx-usecs 10 rx-frames 16

# 높은 처리량 모드
ethtool -C eth0 rx-usecs 100 rx-frames 128

# LRO 활성화/비활성화
ethtool -K eth0 lro on
ethtool -K eth0 lro off  # 라우팅/브리징 시 비활성화 권장

# TSO 확인
ethtool -k eth0 | grep segmentation

# 링 버퍼 크기 조정
ethtool -G eth0 rx 4096 tx 4096

# RSS 해시 키 설정 확인
ethtool -x eth0
vmxnet3 LRO 주의사항: vmxnet3의 LRO는 하드웨어 수준에서 패킷을 합치므로, VM이 라우터나 브리지로 동작하는 경우 LRO를 비활성화해야 합니다. 합쳐진 패킷을 다른 인터페이스로 전달하면 수신 측에서 TSO 정보가 없어 재분할이 불가능한 대형 패킷이 전송될 수 있습니다.
UPT(Uniform Passthrough) 아키텍처

UPT(Uniform Passthrough)는 VMware가 설계한 기술로, ESXi vmkernel을 우회하여 SR-IOV VF로부터 게스트가 패킷을 직접 수신하는 경로다. UPT가 활성화되면 vmkernel의 가상 스위치(vSwitch/DVS)를 거치지 않고 물리 NIC의 VF가 게스트 메모리에 직접 DMA한다.

UPT vs SR-IOV 차이: SR-IOV는 PCIe 표준 기술이며 하이퍼바이저 중립적이다. 반면 UPT는 VMware 전용 구현으로, ESXi vmkernel이 VF 할당, 링크 상태, 라이브 마이그레이션을 중앙에서 조율한다. 게스트 드라이버(vmxnet3) 관점에서는 경로 전환이 투명하게 처리된다.

비교 항목UPT (VMware)SR-IOV (일반)
관리 주체ESXi vmkernel 중앙 관리하이퍼바이저 독립 / PF 드라이버
라이브 마이그레이션지원 (VF → emulation 폴백)제한적 (하드웨어 종속)
성능vmkernel 우회, 저지연최고 수준 (커널 패스 없음)
호환성vmxnet3 드라이버 필수VF 드라이버 필요 (e.g., ixgbevf)
NIC 요구사항VMware 인증 물리 NICSR-IOV 지원 NIC 전체
투명성게스트에 완전 투명게스트가 VF 디바이스 직접 인식

hv_netvsc 심화

hv_netvsc는 Microsoft Hyper-V 및 Azure 환경의 반가상화 네트워크 드라이버입니다. VMBus를 통한 합성(synthetic) 경로와 SR-IOV VF를 통한 가속 네트워킹(Accelerated Networking) 경로를 단일 네트워크 인터페이스 아래에서 투명하게 관리하는 것이 핵심 특징입니다. VF가 있으면 데이터 패킷은 VF를 통해 직접 전송되고, VF가 제거되면 자동으로 합성 경로로 폴백합니다.

Accelerated Networking 아키텍처: Azure의 Accelerated Networking은 Mellanox(ConnectX) 또는 MANA(Microsoft Azure Network Adapter) VF를 VM에 직접 할당합니다. hv_netvsc는 VF 드라이버(mlx5_core 또는 mana)를 netvsc의 하위 디바이스로 등록하여, 상위 계층에서는 단일 인터페이스(eth0)만 보이도록 합니다. 이 투명한 본딩(bonding) 덕분에 라이브 마이그레이션 시에도 네트워크 연결이 유지됩니다.

Azure VM Application 커널 네트워크 스택 hv_netvsc (eth0) 패킷 경로 결정 및 VF 관리 Synthetic Path VMBus 채널 VF Path SR-IOV VF 직접 VMBus Ring Buffer (send/recv rings) VF Driver (mlx5_core / mana) RNDIS/NVSP 프로토콜 VF Hardware (PCIe BAR) Hyper-V Host Azure Virtual Switch SDN, ACL, VFP (Virtual Filtering Platform) NetVSP (합성 백엔드) SR-IOV PF (VF 관리) Physical NIC (Mellanox ConnectX / MANA) Azure Network VMBus PCIe passthrough
그림 52. hv_netvsc 이중 경로 — VMBus 합성 경로(폴백)와 SR-IOV VF 가속 경로(데이터 평면)를 투명하게 관리
VF Failover 매커니즘

hv_netvsc의 핵심 기능 중 하나는 VF failover입니다. Azure 호스트는 라이브 마이그레이션, 호스트 유지보수, 또는 기타 이유로 VF를 동적으로 제거하고 재할당할 수 있습니다. hv_netvsc는 이 과정을 상위 계층에 투명하게 처리합니다: VF가 제거되면 자동으로 합성 경로로 전환하고, VF가 다시 제공되면 가속 경로로 복귀합니다.

VF Failover 시퀀스 — 라이브 마이그레이션 시나리오 시간 정상 동작 VF 활성 VF: 데이터 전송 Synthetic: 대기 40 Gbps (가속) t0 VF 제거 마이그레이션 시작 VF: revoke 이벤트 Synthetic: 활성화 10 Gbps (합성) t1 합성 전용 마이그레이션 진행중 VF: 없음 Synthetic: 전송중 10 Gbps t2 VF 재할당 마이그레이션 완료 VF: offer 이벤트 Synthetic: 대기 전환 전환중 (VF probe) t3 복구 40G t4
그림 53. VF Failover 시퀀스 — VF 제거 → 합성 경로 폴백 → VF 재할당 → 가속 복귀 (애플리케이션 투명)
Accelerated Networking 상세

Azure의 Accelerated Networking은 SR-IOV를 기반으로 하드웨어 데이터 경로를 VM에 직접 노출합니다. 이를 통해 합성 경로 대비 지연 시간이 약 10배 개선되고(~100μs → ~25μs), CPU 사용률이 크게 감소합니다. D/E/F/G/M 시리즈의 대부분의 인스턴스 크기(2+ vCPU)에서 지원됩니다.

합성 경로 vs Accelerated Networking 비교
항목합성 경로 (VMBus)Accelerated Networking (VF)
데이터 경로VM → VMBus → Host vSwitch → NICVM → VF → NIC (호스트 우회)
지연 시간 (p50)~100 μs~25 μs
최대 대역폭인스턴스 한도인스턴스 한도
CPU 사용률높음 (호스트 경유)낮음 (직접 전달)
라이브 마이그레이션항상 가능VF 폴백 후 가능
VF 드라이버불필요mlx5_core / mana
채널/큐 관리

hv_netvsc는 VMBus 채널을 통해 멀티큐를 지원합니다. 각 VMBus 채널은 하나의 CPU에 매핑되며, send/receive 링 버퍼로 구성됩니다. VF가 활성화된 상태에서도 합성 채널은 유지되며, 제어 메시지(RNDIS 제어, 링크 상태 변경 등)에 사용됩니다.

# hv_netvsc 드라이버 정보 확인
ethtool -i eth0
# driver: hv_netvsc
# version: (kernel)

# VF 상태 확인 (Accelerated Networking)
lspci | grep -i "virtual function\|mellanox\|mana"
# 0001:00:02.0 Ethernet controller: Mellanox ConnectX-4 Lx Virtual Function

# netvsc와 VF의 관계 확인
ls -la /sys/class/net/eth0/lower_*
# lower_enP1s0f0 → VF 인터페이스

# 채널 수 확인
ethtool -l eth0
# Combined:     8

# VMBus 링 버퍼 크기 확인
cat /sys/bus/vmbus/devices/*/ring_buffer_size 2>/dev/null | head -5

# 합성 경로 통계
ethtool -S eth0 | grep -E "(vf_|tx_send_full|rx_comp_busy)"
# vf_rx_packets: 12345678
# vf_rx_bytes: 1234567890
# vf_tx_packets: 23456789
# vf_tx_bytes: 2345678901
# tx_send_full: 0

# 채널 수 변경
ethtool -L eth0 combined 16

# RSS 해시 함수 확인
ethtool -x eth0

# 인터럽트 분배 확인
grep -i hyperv /proc/interrupts
Azure 환경 고려사항
Azure 네트워킹 주의사항:
  • MTU: Azure VNet 기본 MTU는 1500입니다. Jumbo Frame(MTU 9000)을 사용하려면 VM과 서브넷 수준에서 모두 설정해야 합니다.
  • NSG 규칙: Accelerated Networking에서도 NSG(Network Security Group) 규칙은 Azure vSwitch의 VFP(Virtual Filtering Platform)에서 적용됩니다.
  • 라이브 마이그레이션: VF 폴백 시 수 초간 합성 경로로 전환되며, TCP 연결은 유지되지만 지연 시간이 일시적으로 증가합니다.
  • DPDK: Azure DPDK는 netvsc PMD를 통해 VF에 직접 접근합니다. --vdev=net_vdev_netvsc0 옵션을 사용합니다.
# Accelerated Networking 활성화 확인 (Azure CLI)
az vm show -g myRG -n myVM --query "networkProfile.networkInterfaces[].enableAcceleratedNetworking"

# VM 내부에서 VF 존재 확인
lspci | grep -i virtual
# 출력이 있으면 Accelerated Networking 활성 상태

# VF failover 이벤트 확인 (dmesg)
dmesg | grep -i "netvsc\|vf\|failover"
# hv_netvsc vmbus_0: VF registering: eth1
# hv_netvsc vmbus_0: Data path switched to VF: eth1

# 링 버퍼 크기 튜닝 (합성 경로 성능 향상)
ethtool -G eth0 rx 4096 tx 4096

# XDP 프로그램 로드 (hv_netvsc XDP 지원, 5.6+)
ip link set dev eth0 xdpdrv obj xdp_prog.o sec xdp

# MANA VF 상태 확인 (최신 Azure VM)
dmesg | grep mana
# mana 7870:00:02.0: MANA device probed successfully
hv_netvsc 디버깅 팁: VF failover 문제 진단 시 dmesg에서 "Data path switched" 메시지를 확인하세요. 합성 경로의 성능이 예상보다 낮다면 VMBus 링 버퍼 크기를 늘리고(ethtool -G), 채널 수가 vCPU 수와 일치하는지 확인하세요. Azure 직렬 콘솔(az serial-console connect)을 통해 네트워크 단절 시에도 VM에 접근할 수 있습니다.

제조사별 공통 디버깅 루틴

  1. 드라이버/펌웨어 버전 고정
    ethtool -i 결과를 티켓/배포 메타데이터에 저장
  2. 벤더 통계 키 추출
    ethtool -S에서 reset/drop/queue 계열 카운터를 표준화
  3. link/queue 이벤트 타임라인화
    dmesg, devlink health, orchestrator 이벤트를 같은 타임라인으로 병합
  4. fallback 경로 검증
    오프로드 비활성화 후 재현 여부를 확인해 HW/드라이버/스택 원인을 분리
  5. 벤더별 재현 스크립트 유지
    링크 flap, reset storm, queue resize, offload toggle 시나리오를 자동화
권장 운영 전략: “공통 net_device 체크리스트 + 벤더별 확장 체크리스트” 2단 구조로 운영하세요. 공통 지표만 보면 벤더 특이 장애를 놓치기 쉽고, 벤더 전용 지표만 보면 스택 공통 회귀를 놓치기 쉽습니다.

드라이버 간 종합 비교

리눅스 커널에는 수십 개의 네트워크 디바이스 드라이버가 존재하며, 각각 고유한 아키텍처 결정과 최적화 전략을 채택하고 있습니다. 이 섹션에서는 주요 드라이버를 기능, 아키텍처, 큐 모델, 인터럽트 처리, 메모리 관리 등 다양한 축으로 비교 분석합니다. 드라이버 선택 시 워크로드 특성에 따른 최적 선택을 돕기 위한 종합 레퍼런스입니다.

전체 기능 매트릭스

아래 표는 리눅스 커널 6.x 기준 주요 네트워크 드라이버의 핵심 기능 지원 현황을 정리한 것입니다. ✓는 완전 지원, △는 부분 지원 또는 제한적 지원, ✗는 미지원을 나타냅니다.

드라이버 벤더/칩 XDP Native AF_XDP ZC SR-IOV (max VFs) tc flower offload devlink RDMA PTP HW TS page_pool BQL
ice Intel E810 ✓ (256)
mlx5e Mellanox/NVIDIA CX-5/6/7 ✓ (1024)
bnxt_en Broadcom NetXtreme-E ✓ (512)
cxgb4 Chelsio T4/T5/T6 ✓ (128)
ena Amazon ENA
gve Google gVNIC
virtio_net QEMU/KVM virtio
vmxnet3 VMware VMXNET3
hv_netvsc Hyper-V NetVSC
r8169 Realtek RTL8111/8168
atlantic Aquantia AQC
mvpp2 Marvell PPv2
dpaa2 NXP DPAA2 (LX2160A)
기능 지원 vs 성숙도: 표에서 ✓로 표시된 기능이라도 드라이버마다 구현 성숙도가 다릅니다. 예를 들어 mlx5e의 tc flower offload는 수백 개의 매치 필드를 지원하는 반면, 다른 드라이버는 기본적인 5-tuple 매칭만 지원할 수 있습니다. 프로덕션 배포 전 반드시 해당 드라이버의 구체적인 기능 범위를 검증해야 합니다.

각 드라이버의 고급 기능 지원 상세 비교입니다.

드라이버 XDP Multi-buffer XDP HW Offload switchdev TLS HW Offload RSS Hash Configurable Flow Director Queue Resize (online)
ice
mlx5e
bnxt_en
cxgb4
ena
gve
virtio_net
vmxnet3
hv_netvsc
r8169
atlantic
mvpp2
dpaa2

기능 성숙도 계층

기능 지원 여부(✓/✗)만으로는 드라이버의 실제 준비 상태를 평가하기 어렵습니다. 같은 ✓라도 프로덕션 배포에서 검증된 기능과 최근 추가된 실험적 구현 사이에는 큰 차이가 있습니다.

성숙도 레벨 정의:
  • Production: 메인라인 포함 2년 이상, 광범위하게 배포되어 사용 중, 활발히 유지보수됨
  • Stable: 메인라인 포함, 기본 테스트 완료, 일부 프로덕션 배포 사례 있음
  • Experimental: 최근 추가되었거나 테스트가 제한적임, API 변경 가능성 있음
  • Stub: 최소 구현만 존재, 프로덕션 미적합
드라이버 XDP basic AF_XDP ZC tc flower SR-IOV devlink page_pool HW timestamping
mlx5e Production (5.2) Production (5.5) Production (4.18) Production (4.14) Production (5.2) Production (5.9) Production (4.10)
ice Production (5.5) Production (5.10) Production (5.7) Production (5.5) Production (5.5) Production (5.14) Production (5.5)
bnxt_en Production (5.9) Production (5.12) Stable (5.8) Production (4.6) Production (5.4) Stable (5.18) Production (4.6)
ena Stable (5.6) Stub (—) Stub (—) Stub (—) Stub (—) Production (5.12) Stub (—)
gve Stable (5.16) Stub (—) Stub (—) Stub (—) Stub (—) Stable (5.16) Stub (—)
r8169 Stub (—) Stub (—) Stub (—) Stub (—) Stub (—) Production (5.10) Stub (—)
atlantic Stable (5.4) Stub (—) Stub (—) Stub (—) Stub (—) Stable (5.11) Stable (5.4)
virtio_net Production (4.12) Stub (—) Stub (—) Stub (—) Stub (—) Stable (5.19) Stub (—)
vmxnet3 Stable (5.5) Stub (—) Stub (—) Stub (—) Stub (—) Stub (—) Stub (—)
cxgb4 Experimental (5.12) Stub (—) Stable (4.18) Production (3.12) Stub (—) Stub (—) Production (4.4)
범위 면책 조항 — tc flower offload: mlx5e는 200개 이상의 매치 필드(VLAN, MPLS, TCP 플래그, IP TOS 등)를 tc flower offload로 지원하는 반면, ena는 tc flower offload 지원이 전혀 없습니다. 같은 "지원"이라도 실제 적용 범위가 크게 다를 수 있으므로, 프로덕션 배포 전에 반드시 대상 드라이버의 구체적인 offload 범위를 tc filter show dev <intf> ingress와 커널 소스에서 검증하십시오.

아키텍처 패턴 비교

네트워크 드라이버의 아키텍처는 크게 중앙 집중형(Centralized)분산형(Distributed)으로 분류할 수 있습니다. 중앙 집중형은 펌웨어 컨트롤러가 패킷 처리 파이프라인 전체를 관장하며, 드라이버는 펌웨어에 명령을 전달하는 역할에 그칩니다. 반면 분산형은 하드웨어 파이프라인이 고정된 기능 블록으로 구성되고, 드라이버가 각 블록을 직접 프로그래밍합니다.

중앙 집중형 vs 분산형 아키텍처 패턴 중앙 집중형 (Firmware-Centric) bnxt_en, i40e, ena, gve 드라이버 (Thin Shim Layer) HWRM 명령 펌웨어 컨트롤러 패킷 분류 엔진 RSS/Flow Director Offload 처리 큐 관리자 DMA 엔진 / 스케줄러 NIC 하드웨어 (MAC/PHY) 분산형 (Hardware Pipeline) mlx5e, ixgbe, ice 드라이버 (Rich Control Plane) Flow Steering TC Offload eSwitch 레지스터 직접 접근 Flow Table HW Match/Action Steering Unit 최소 펌웨어 (초기화/에러 처리만) TxQ DMA RxQ DMA CQ DMA MAC/PHY ■ 펌웨어 ■ 하드웨어 블록 ■ 드라이버 소프트웨어
그림 ndd-54. 중앙 집중형(펌웨어 중심) vs 분산형(하드웨어 파이프라인) 아키텍처 비교

아키텍처 선택은 드라이버의 복잡도, 디버깅 용이성, 기능 확장성에 직접적인 영향을 미칩니다.

특성 중앙 집중형 (Firmware-Centric) 분산형 (Hardware Pipeline)
드라이버 복잡도 낮음 — 펌웨어 API 호출 위주 높음 — HW 레지스터 직접 관리
디버깅 난이도 높음 — 펌웨어 내부 상태 불투명 낮음 — 레지스터 덤프로 상태 확인 가능
기능 확장 펌웨어 업데이트 필요 (벤더 의존) 드라이버 코드 변경으로 가능
초기화 시간 길음 — 펌웨어 로딩/핸드셰이크 필요 짧음 — 레지스터 직접 설정
에러 복구 펌웨어 리셋 필요 (전체 중단 가능) 세분화된 복구 가능 (큐 단위)
대표 드라이버 bnxt_en, ena, gve, i40e mlx5e, ixgbe, ice (하이브리드)

Control plane 설계 패턴도 드라이버마다 크게 다릅니다.

패턴 설명 적용 드라이버
Mailbox Command 공유 메모리 메일박스를 통해 펌웨어와 명령/응답 교환 bnxt_en (HWRM), ena (Admin Queue)
Admin Queue 전용 관리 큐를 통한 비동기 명령 전달 ice (AQ), i40e (AQ), idpf
Command Interface 레지스터 기반 명령 인터페이스로 HW 직접 제어 mlx5e (cmdif), ixgbe
Hypervisor Channel VMBus/virtqueue를 통한 호스트 드라이버 통신 hv_netvsc (VMBus), virtio_net (virtqueue)

아키텍처 설계 결정의 이유

각 드라이버가 특정 아키텍처를 선택한 데는 하드웨어 설계, 타깃 시장, 성능 목표, 소프트웨어 생태계 등 복합적인 이유가 있습니다. 단순한 "좋은 설계 vs 나쁜 설계"가 아니라, 각자의 제약 조건 안에서 최적화된 선택입니다.

mlx5 통합 CQ 모델 — RDMA heritage
mlx5e가 TX와 RX에 각각 독립된 CQ(Completion Queue)를 두는 이유는 InfiniBand/RDMA에서 비롯됩니다. RDMA 연산은 Send/Receive/RDMA Read/Write 4가지 동작이 모두 동일한 CQ에 완료 이벤트를 올려야 하기 때문에, 처음부터 "작업 큐(WQ) ↔ 완료 큐(CQ) 1:1" 구조로 설계되었습니다. 이 구조 덕분에 mlx5e는 NIC와 RDMA를 동일한 하드웨어에서 공유 CQ 폴링으로 처리할 수 있어, 인터럽트 오버헤드를 최소화하면서도 RDMA와 Ethernet 공존이 가능합니다.
/* drivers/net/ethernet/mellanox/mlx5/core/en.h */
struct mlx5e_txqsq {
    struct mlx5e_cq      cq;   /* Tx 전용 CQ — WQ와 1:1 대응 */
    struct mlx5_wq_cyc   wq;   /* Work Queue (송신 디스크립터 링) */
    ...
};
struct mlx5e_rq {
    struct mlx5e_cq      cq;   /* Rx 전용 CQ — RDMA MR 처리와 분리 */
    struct mlx5_wq_ll    wq;   /* Linked-list WQ (MPWRQ 지원) */
    ...
};
bnxt HWRM 선택 이유 — 프로그래머블 펌웨어
Broadcom NetXtreme-E의 HWRM(Hardware Resource Manager)은 드라이버 변경 없이 펌웨어 업데이트만으로 기능 추가가 가능하도록 설계되었습니다. HWRM은 커맨드 버전 필드를 포함하기 때문에 드라이버와 펌웨어 간 forward compatibility를 보장하며, NIC를 OEM에 공급할 때 펌웨어 커스터마이징만으로 다양한 SKU를 지원할 수 있습니다.
/* drivers/net/ethernet/broadcom/bnxt/bnxt_hwrm.h */
struct hwrm_cmd_req_hdr {
    __le16 req_type;      /* 커맨드 타입 */
    __le16 cmpl_ring;     /* 완료 링 ID */
    __le16 seq_id;        /* 시퀀스 번호 — 응답 매칭용 */
    __le16 target_id;     /* 타깃 기능 ID (VF/PF 구분) */
    __le64 resp_addr;     /* DMA 응답 버퍼 주소 */
};
결과적으로 bnxt 드라이버 코드는 상대적으로 작고 단순하며, 복잡한 하드웨어 로직은 펌웨어에 위임됩니다.
ice AdminQ 분리 이유 — IPU/DPU 수렴 전략
Intel E810의 AdminQ(Administration Queue)는 단순한 명령 채널이 아니라, Intel의 IPU(Infrastructure Processing Unit) 및 DPU 전략과 연결됩니다. AdminQ를 데이터 경로와 분리함으로써 동일한 제어 평면 코드를 NIC, IPU, DPU 세 가지 배포 모델에서 재사용할 수 있습니다. 또한 AdminQ는 비동기 이벤트(링크 상태 변화, 에러 보고 등)를 데이터 큐와 완전히 독립적으로 처리하여, 고부하 상황에서도 제어 명령이 손실되지 않도록 합니다.
/* drivers/net/ethernet/intel/ice/ice_adminq_cmd.h */
struct ice_aq_desc {
    __le16 flags;     /* ICE_AQ_FLAG_RD — 데이터 방향 */
    __le16 opcode;    /* 커맨드 opcode */
    __le16 datalen;   /* 데이터 길이 */
    __le16 retval;    /* 펌웨어 응답 코드 */
    __le32 cookie_h;
    __le32 cookie_l;  /* 드라이버 쿠키 (비동기 완료 식별용) */
    union ice_aqc_one_q params; /* 커맨드별 파라미터 */
};
ENA NVMe-like 큐 모델 이유 — 가상화 최적화
Amazon ENA(Elastic Network Adapter)가 NVMe의 SQ(Submission Queue)/CQ(Completion Queue) 모델을 차용한 이유는 VM exit 오버헤드 최소화입니다. NVMe SQ/CQ 모델은 게스트 VM이 큐 tail pointer를 doorbell 레지스터에 쓰는 것만으로 작업을 제출할 수 있어, 하이퍼바이저 개입 없이 직접 하드웨어에 접근하는 것과 유사한 효율을 냅니다. 또한 AWS 커스텀 실리콘 설계 시 NVMe 검증 경험을 재활용할 수 있었습니다.
/* drivers/net/ethernet/amazon/ena/ena_com.h */
struct ena_com_io_sq {
    struct ena_com_io_desc *desc_addr; /* SQ 디스크립터 링 */
    u32 __iomem  *db_addr;             /* Doorbell 레지스터 주소 */
    u16           q_depth;             /* 큐 깊이 */
    u16           tail;                /* 제출 tail — VM exit 없이 쓰기 */
};
virtio split vs packed ring — 캐시라인 최적화의 역사
virtio의 split ring(레거시)은 Descriptor Table, Available Ring, Used Ring 3개 배열로 구성되어 있어, 게스트가 패킷을 제출할 때 Descriptor Table과 Available Ring을 모두 갱신해야 합니다. 이 두 자료구조가 서로 다른 캐시라인에 위치할 경우 캐시 바운싱(cache bouncing)이 발생합니다. Linux 5.0에서 도입된 packed ring은 단일 배열에 디스크립터와 완료 상태를 함께 저장하고, wrap counter를 이용해 head/tail을 추적함으로써 캐시 미스를 크게 줄였습니다.
/* include/uapi/linux/virtio_ring.h — split ring (레거시) */
struct vring {
    unsigned int num;
    struct vring_desc  *desc;   /* 디스크립터 배열 — 별도 캐시라인 */
    struct vring_avail *avail;  /* Available ring — 또 다른 캐시라인 */
    struct vring_used  *used;   /* Used ring — 세 번째 캐시라인 오염 */
};

/* packed ring (5.0+) — 단일 배열, wrap counter 방식 */
struct vring_packed_desc {
    __le64 addr;    /* 버퍼 주소 */
    __le32 len;     /* 버퍼 길이 */
    __le16 id;      /* 디스크립터 ID */
    __le16 flags;   /* AVAIL/USED 비트 + wrap counter — 동일 캐시라인 */
};

커널 버전별 기능 타임라인

네트워크 드라이버의 주요 기능은 커널 버전마다 단계적으로 추가되었습니다. 아래 타임라인은 XDP, AF_XDP, page_pool, devlink 등 핵심 기능의 도입 이력을 드라이버별로 정리합니다.

커널 버전 출시 시기 주요 드라이버 변경사항
5.0 2019-03 virtio packed ring 도입 (virtio_net); page_pool API 초기 도입; XDP multi-buffer 기반 작업 시작
5.1 2019-05 mlx5e AF_XDP zero-copy 초기 지원; ice 드라이버 메인라인 합류; devlink port 기능 확장
5.2 2019-07 mlx5e XDP native 안정화; bnxt_en devlink health reporter 추가; ena XDP 준비 작업
5.3 2019-09 ixgbe AF_XDP zero-copy 추가; mlx5e TC flower multi-action offload; ice SR-IOV 초기 지원
5.4 2019-11 bnxt_en devlink eswitch 모드; atlantic XDP native 추가; cxgb4 TC flower 확장
5.5 2020-01 mlx5e AF_XDP ZC Production 안정화; ice XDP native + AF_XDP ZC; vmxnet3 XDP 지원
5.6 2020-03 ena XDP native 추가; AF_XDP 소켓 통계 개선; mlx5e HW timestamps for AF_XDP
5.9 2020-10 mlx5e page_pool 전환 완료; bnxt_en XDP native 안정화; XDP multi-buffer 프레임워크 추가
5.10 2020-12 ice AF_XDP ZC Production; r8169 page_pool 전환; mlx5e XDP multi-buffer 초기 지원
5.12 2021-04 bnxt_en AF_XDP ZC 추가; ena page_pool 전환; cxgb4 XDP 실험적 지원
5.14 2021-08 ice page_pool 전환; mlx5e XDP multi-buffer Production; devlink rate 기능 추가
5.16 2022-01 gve XDP native + page_pool; virtio_net XDP multi-buffer 지원; bnxt_en page_pool 안정화
5.19 2022-07 virtio_net page_pool 지원; ice GNSS (GPS 타임스탬프) 지원; AF_XDP 멀티큐 개선
6.0 2022-10 mlx5e devlink rate 계층 구조; ice switchdev LAG; XDP hints (메타데이터 API) 도입
6.1 2022-12 XDP hints Production; bnxt_en XDP hints 지원; ice DDP (Dynamic Device Personalization) 확장
6.4 2023-06 mlx5e SF(Sub-Function) devlink 안정화; gve AF_XDP ZC 추가; virtio_net packed ring 성능 개선
6.6 2023-10 netdev queue API (큐 단위 통계) 도입; ice IDPF 기반 가상화 지원; devlink selftests 프레임워크
6.8 2024-03 mlx5e HW GRO offload; ena 버스트 큐 최적화; XDP 리디렉션 성능 개선
특정 기능의 커널 버전 호환성 확인 방법:
# 현재 커널에서 드라이버의 XDP 지원 확인
ethtool --show-features eth0 | grep xdp

# page_pool 통계 확인 (5.9+ 필요)
ethtool -S eth0 | grep -i page_pool

# AF_XDP ZC 지원 여부 — 실제 소켓 생성으로 확인
ip link show eth0  # XDP 플래그 확인

# 커널 소스에서 드라이버 기능 도입 버전 추적
git log --oneline drivers/net/ethernet/mellanox/mlx5/core/en_main.c | head -20

# devlink 기능 확인
devlink dev info pci/0000:XX:00.0
커널 버전이 기능 도입 버전보다 낮으면 해당 기능을 사용할 수 없습니다. 배포 환경의 커널 버전(uname -r)을 반드시 확인하십시오.

큐 모델 분류

네트워크 드라이버의 큐 모델은 성능 확장성과 CPU 활용 효율에 결정적 영향을 미칩니다. 단일 큐에서 시작하여 대칭 멀티큐, 분리 큐, 완료 큐 독립 모델로 진화해 왔습니다.

네트워크 드라이버 큐 모델 분류 체계 큐 모델 (Queue Model) 단일 큐 1 TxQ + 1 RxQ e1000e, r8169 대칭 멀티큐 N×(TxQ + RxQ) 대칭 igb, ixgbe, i40e 분리 큐 TxQ ≠ RxQ 비대칭 idpf, ice (Gen3) CQ 분리형 독립 Completion Queue bnxt, mlx5e 특징 • 단일 NAPI 인스턴스 • 글로벌 Tx 락 필요 • CPU 확장 불가 • 1Gbps 이하 전용 특징 • CPU당 TxQ+RxQ 쌍 • RSS로 RxQ 분산 • NAPI per queue-pair • 10~40Gbps 적합 특징 • Tx/Rx 큐 수 독립 조절 • Tx Completion 큐 분리 • Flow-based 스케줄링 • 100Gbps+ 최적화 특징 • CQ가 Tx/Rx 완료 통합 • CQ → EQ 이벤트 전달 • 유연한 인터럽트 매핑 • RDMA 통합 최적 성능 확장성 증가 방향 → 단순 · 저속 복잡 · 고속
그림 ndd-55. 네트워크 드라이버 큐 모델 분류 체계 — 단일 큐에서 CQ 분리형까지의 진화

링 구조와 디스크립터 포맷도 드라이버마다 상이합니다.

드라이버 Tx 디스크립터 크기 Rx 디스크립터 크기 CQ 존재 기본 링 크기 최대 링 크기 디스크립터 타입
ice 16B 32B ✗ (인라인) 1024 8192 Flex descriptor
mlx5e 64B (WQE) 변동 (MPWQE) ✓ (64B CQE) 1024 8192 WQE/CQE
bnxt_en 16B 16B ✓ (32B CQE) 512 32768 BD (Buffer Descriptor)
ixgbe 16B 16/32B 512 4096 Advanced descriptor
ena 16B 16B 1024 16384 SQ/CQ descriptor
virtio_net 가변 (SG) 가변 (SG) ✓ (Used Ring) 256 32768 Virtqueue descriptor
r8169 16B 16B 256 1024 Legacy descriptor

인터럽트 처리 비교

MSI-X 벡터 할당 전략은 드라이버 성능에 큰 영향을 미칩니다. 관리용 벡터와 데이터 큐 벡터의 분리 방식, 공유 여부가 드라이버마다 다릅니다.

MSI-X 벡터 할당 전략 비교 Per-Queue 독립 벡터 ixgbe, igb, ice MSI-X Vec 0 ← Admin/Misc MSI-X Vec 1 → TxQ0+RxQ0 MSI-X Vec 2 → TxQ1+RxQ1 MSI-X Vec 3 → TxQ2+RxQ2 ... MSI-X Vec N → TxQN+RxQN ✓ 간단한 IRQ affinity ✓ 큐 쌍 단위 NAPI ✗ 벡터 수 = 큐 수 + 1 ✗ 벡터 많이 소비 공유 벡터 모델 cxgb4, vmxnet3 MSI-X Vec 0 ← Non-data MSI-X Vec 1 RxQ0 RxQ1 MSI-X Vec 2 RxQ2 RxQ3 MSI-X Vec 3 TxQ 전체 공유 ✓ 적은 벡터로 운용 ✓ VM 환경 벡터 제한 대응 ✗ 큐 간 인터럽트 간섭 ✗ CPU affinity 세밀 조정 불가 EQ/CQ 기반 분리 mlx5e, bnxt_en MSI-X Vec 0 ← Async Events MSI-X Vec 1 ← Command CQ EQ 0 → CQ0(Tx) + CQ1(Rx) EQ 1 → CQ2(Tx) + CQ3(Rx) EQ N → CQ_N(Tx+Rx) ✓ 유연한 CQ→EQ 매핑 ✓ RDMA/Storage CQ 공유 가능 ✓ 관리/데이터 완전 분리 ✗ 초기 설정 복잡도 높음
그림 ndd-56. MSI-X 벡터 할당 전략 비교 — Per-Queue 독립, 공유 벡터, EQ/CQ 기반 분리

인터럽트 코얼레싱(coalescing) 파라미터도 드라이버 간 차이가 큽니다.

드라이버 Rx usecs 기본값 Tx usecs 기본값 Adaptive 지원 Adaptive 알고리즘 CQ 기반 모더레이션
ice 50 µs 50 µs DIM (Dynamically-tuned Interrupt Moderation)
mlx5e 8 µs 16 µs DIM + HW 자체 모더레이션
bnxt_en 16 µs 16 µs DIM
ixgbe 20 µs 72 µs 커널 DIM 프레임워크
ena 20 µs 64 µs 자체 적응형 (ENA DIM)
virtio_net N/A N/A
r8169 고정 고정
DIM 프레임워크: 커널 5.x 이후 net/core/dim/에 통합된 DIM(Dynamically-tuned Interrupt Moderation) 프레임워크는 패킷 수와 바이트 수를 기반으로 인터럽트 간격을 자동 조정합니다. ice, mlx5e, bnxt_en 등 최신 드라이버는 모두 이 공통 프레임워크를 활용하며, ethtool -C <dev> adaptive-rx on으로 활성화합니다.

메모리 모델 비교

DMA 매핑 수명주기와 버퍼 할당 전략은 드라이버 성능의 핵심 결정 요소입니다. 패킷당 매핑/해제 방식에서 사전 매핑 풀 방식으로 진화하면서 CPU 오버헤드가 크게 감소했습니다.

DMA 매핑 수명주기 비교 alloc dma_map RX 사용 dma_unmap free/recycle 패킷당 (r8169) alloc map HW unmap free 매 패킷 사전매핑 (ixgbe) init 시 alloc + map (1회) HW sync 재사용 (unmap 없이) init 1회 page_pool (mlx5e 등) pool alloc+map 풀에서 가져옴 HW skb build page_pool 자동 재활용 (DMA 매핑 유지) CPU 비용 높은 작업 경량/캐시 친화적 작업 → 재활용 경로 (CPU 비용 최소)
그림 ndd-57. DMA 매핑 수명주기 비교 — 패킷당 map/unmap, 사전 매핑, page_pool 재활용

page_pool 도입 현황과 버퍼 할당 전략을 비교합니다.

드라이버 page_pool 사용 할당 단위 버퍼 전략 DMA 방향 재활용 방식
mlx5e PAGE (4KB/64KB) MPWQE (Multi-Packet WQE) DMA_FROM_DEVICE page_pool + frag 재활용
ice PAGE (4KB) 1 page per descriptor DMA_FROM_DEVICE page_pool
bnxt_en PAGE frag page frag + aggregation DMA_FROM_DEVICE page_pool
ixgbe △ (전환 중) half-page 페이지 분할 (2KB×2) DMA_FROM_DEVICE 자체 page flip
ena PAGE Large buffer mode DMA_FROM_DEVICE page_pool
virtio_net PAGE frag mergeable buffer DMA_FROM_DEVICE page_pool
r8169 PAGE frag Single buffer DMA_FROM_DEVICE page_pool
vmxnet3 SKB data alloc_skb per packet DMA_FROM_DEVICE 없음 (매번 free)
hv_netvsc VMBus buffer 공유 메모리 링 N/A (VMBus) VMBus 재활용
MPWQE (Multi-Packet WQE): mlx5e의 고유 최적화로, 하나의 WQE(Work Queue Element)가 여러 패킷을 수용할 수 있는 큰 버퍼(예: 64KB)를 가리킵니다. 하드웨어가 패킷을 연속으로 채우고 CQE에서 stride 정보를 제공하여 디스크립터 소비를 극적으로 줄입니다. 특히 소형 패킷이 대량으로 도착하는 워크로드에서 효과적입니다.

펌웨어 의존성 레벨 비교

네트워크 드라이버의 펌웨어 의존도는 기능성, 디버깅 용이성, 장애 복구 시간에 직접적 영향을 미칩니다. 다음 표는 주요 드라이버를 펌웨어 의존도별로 분류합니다.

레벨 드라이버 펌웨어 역할 디버깅 난이도 복구 시간 기능 업데이트 방법
None r8169, tg3 없음 (레지스터 직접 접근) 낮음 <1초 드라이버 코드 수정
Light igb, ixgbe PHY 초기화, NVM 관리 낮음 1~2초 드라이버 + 선택적 FW 업데이트
Medium ice, i40e Admin Queue 명령 처리, 일부 오프로드 중간 3~5초 DDP 프로필 + FW 업데이트
Heavy bnxt_en, mlx5e 패킷 분류, VXLAN/GRE 오프로드, eSwitch 높음 5~15초 펌웨어 필수 업데이트
Critical ena, gve 데이터 경로 전체 (하이퍼바이저 펌웨어) 매우 높음 N/A (클라우드 관리) 클라우드 프로바이더 의존
펌웨어 장애 시 영향: Heavy/Critical 레벨 드라이버에서 펌웨어 크래시가 발생하면 전체 NIC가 비응답 상태가 될 수 있습니다. bnxt_en의 경우 devlink health를 통해 펌웨어 상태를 모니터링하고 자동 복구를 시도하지만, ena/gve는 인스턴스 재시작이 유일한 복구 수단일 수 있습니다.
# 펌웨어 버전 및 상태 확인
ethtool -i eth0 | grep firmware

# devlink 기반 펌웨어 상태 모니터링 (bnxt, mlx5, ice)
devlink health show pci/0000:03:00.0

# 펌웨어 리포터 진단 정보 확인
devlink health diagnose pci/0000:03:00.0 reporter fw

# 펌웨어 업데이트 (devlink 지원 드라이버)
devlink dev flash pci/0000:03:00.0 file firmware.bin

리셋 전략 비교

NIC 장애 복구에서 리셋 세분화 수준은 서비스 가용성에 직접적 영향을 미칩니다. 최신 드라이버일수록 전체 NIC 리셋 없이 개별 큐나 VF 단위의 세밀한 복구를 지원합니다.

리셋 세분화 수준 및 드라이버별 지원 범위 Global Reset 전체 NIC 리셋 모든 PF/VF 중단 복구 시간: 5~30초 r8169, tg3, vmxnet3 PF Reset Physical Function 리셋 해당 PF의 VF만 영향 복구 시간: 2~5초 ice, i40e, bnxt_en VF Reset Virtual Function 리셋 단일 VF만 영향 복구 시간: <1초 mlx5e, ice, bnxt_en Queue Reset 개별 큐 리셋 트래픽 중단 최소 복구 시간: <100ms mlx5e, ice (Gen3) 세분화 수준 증가 → 영향 범위 감소 → 복구 시간 단축 드라이버별 지원 범위 mlx5e Global → PF → VF → Queue (전체 지원) ice Global → PF → VF → Queue (Gen3 이후) bnxt_en Global → PF → VF (큐 단위 미지원) i40e Global → PF (VF 리셋 제한적) r8169 Global만 지원 ena Device Reset (하이퍼바이저 제어)
그림 ndd-58. 리셋 세분화 수준 및 드라이버별 지원 범위
드라이버 Global Reset PF Reset VF Reset Queue Reset Tx Timeout 처리 Watchdog 자동 복구
ice ✓ (Gen3) PF 리셋
mlx5e 큐 리셋 시도 → PF 리셋
bnxt_en PF 리셋 ✓ (devlink health)
i40e PF 리셋
ena N/A N/A 디바이스 리셋 ✓ (keep-alive)
virtio_net N/A N/A virtqueue 리셋
r8169 N/A N/A 전체 리셋 ✓ (링크 watchdog)
# 리셋 통계 확인 (ethtool)
ethtool -S eth0 | grep -i reset

# devlink health 리셋 이력 (ice, mlx5, bnxt)
devlink health show pci/0000:03:00.0

# 수동 리셋 트리거 (디버깅용)
devlink dev reload pci/0000:03:00.0

# VF 리셋 (SR-IOV 환경)
echo 1 > /sys/bus/pci/devices/0000:03:00.2/reset

성능 심층 분석

네트워크 드라이버의 실제 성능은 코얼레싱 설정, 링 버퍼 크기, NUMA 배치, XDP 지원 수준 등 다양한 요소의 조합에 의해 결정됩니다. 이 섹션에서는 각 요소를 심층 분석하고 드라이버 간 성능 차이의 근본 원인을 파악합니다.

벤치마크 방법론

드라이버 성능 측정은 재현 가능한 환경과 올바른 도구 선택이 핵심입니다. 잘못된 측정 방법은 최대 수십 배의 오차를 초래하며, 최적화 방향을 완전히 잘못 이끌 수 있습니다.

테스트 환경 명세 템플릿

성능 측정 결과를 공유하거나 재현하려면 아래 항목을 반드시 명시해야 합니다. 특히 BIOS 설정은 결과에 수십 % 영향을 미치므로 절대 생략할 수 없습니다.

분류 필수 항목 확인 명령 / 경로 성능 영향
CPU 모델, 코어 수, 클럭, NUMA 토폴로지 lscpu, numactl --hardware 매우 높음
커널 버전, CONFIG_HZ, CONFIG_PREEMPT, RPS/RFS 여부 uname -r, zcat /proc/config.gz 높음
NIC 펌웨어 드라이버 버전, FW 버전, NVM 버전 ethtool -i eth0 중간
BIOS: C-states C1E/C3/C6 비활성화 여부 cpupower idle-info, /sys/devices/system/cpu/cpu*/cpuidle/ 매우 높음 (수십%)
BIOS: SMT/HT Hyper-Threading 활성화 여부 lscpu | grep Thread 높음
BIOS: IOMMU IOMMU 활성화 여부 (DMAR on/off) dmesg | grep IOMMU 중간~높음
메모리 용량, 채널 수, 속도, NUMA 배치 dmidecode -t memory 중간
PCIe Gen/레인 수, ACS 설정 lspci -vvv | grep -i width 중간
OS 배포판, glibc 버전, IRQ 밸런싱 데몬 cat /etc/os-release 낮음~중간
# 전체 환경 스냅샷 원라이너 (벤치마크 기록용)
echo "=== CPU ===" && lscpu | grep -E 'Model|CPU\(s\)|Socket|NUMA|MHz'
echo "=== Kernel ===" && uname -a
echo "=== NIC ===" && ethtool -i eth0
echo "=== C-states ===" && cpupower idle-info 2>/dev/null
echo "=== IOMMU ===" && dmesg | grep -i iommu | head -5
echo "=== IRQ balance ===" && systemctl is-active irqbalance

perf/ftrace/bpftrace 방법론

올바른 계측 도구를 선택하는 것은 측정 오버헤드와 가시성 간 균형을 결정합니다. perf stat는 전체 통계를, ftrace는 경로 추적을, bpftrace는 동적 히스토그램 수집을 제공합니다.

## perf stat: NIC 드라이버 프로파일링 레시피
# 코어 고정 후 인터럽트 통계 수집 (5초)
taskset -c 2 perf stat -e \
  net:netif_receive_skb,net:net_dev_xmit,\
  irq:irq_handler_entry,irq:softirq_raise,\
  cache-misses,cache-references,cycles,instructions \
  -I 1000 -C 2 sleep 5

# 드라이버 함수 핫스팟 (perf record + report)
perf record -C 2 -g -e cycles:u -- sleep 5
perf report --stdio --no-children | head -40

# NAPI poll 지연 분포 측정
perf record -e 'net:napi_poll' -C 2 sleep 5
perf script | awk '{print $NF}' | sort -n | uniq -c
## ftrace: NAPI → GRO 경로 추적 (function_graph)
echo 0 > /sys/kernel/debug/tracing/tracing_on
echo function_graph > /sys/kernel/debug/tracing/current_tracer
echo 'napi_poll' > /sys/kernel/debug/tracing/set_graph_function
echo 'napi_gro_receive' >> /sys/kernel/debug/tracing/set_graph_function
echo 1 > /sys/kernel/debug/tracing/tracing_on
sleep 1
echo 0 > /sys/kernel/debug/tracing/tracing_on
cat /sys/kernel/debug/tracing/trace | head -80

# mlx5e 전용: poll 함수 그래프
echo 'mlx5e_napi_poll' > /sys/kernel/debug/tracing/set_graph_function
echo 1 > /sys/kernel/debug/tracing/tracing_on
sleep 0.1
echo 0 > /sys/kernel/debug/tracing/tracing_on
cat /sys/kernel/debug/tracing/trace
## bpftrace: 드라이버별 NAPI poll 지연 히스토그램
bpftrace -e '
kprobe:napi_poll {
  @start[arg0] = nsecs;
}
kretprobe:napi_poll /@start[arg0]/ {
  @latency_us = hist((nsecs - @start[arg0]) / 1000);
  delete(@start[arg0]);
}
interval:s:5 { print(@latency_us); clear(@latency_us); exit(); }
'

## p50/p95/p99 신뢰도: 30초 이상 수집, warm-up 10초 제외
bpftrace -e '
kprobe:napi_poll { @s[arg0] = nsecs; }
kretprobe:napi_poll /@s[arg0]/ {
  $lat = (nsecs - @s[arg0]) / 1000;
  @dist = lhist($lat, 0, 1000, 10);
  delete(@s[arg0]);
}
interval:s:30 { print(@dist); exit(); }'
통계 신뢰도 체크리스트:
  • 웜업: 측정 전 최소 10초 트래픽으로 TLB/캐시 워밍
  • 반복 횟수: 최소 3회 측정, 최고·최저 제외 후 중간값 사용
  • 이상치 제거: p99.9 이상은 OS 스케줄러/SMI 인터럽트 영향으로 분리 분석
  • 동시 모니터링 금지: top, htop, sar 등 계측 도구 자체가 캐시 오염 유발

패킷 생성기 선택 가이드

워크로드와 측정 목적에 맞는 패킷 생성기 선택이 중요합니다. 잘못된 생성기 선택은 DUT(Device Under Test)가 아닌 생성기 자체가 병목이 되는 결과를 초래합니다.

생성기 최대 속도 유연성 설치 난이도 single-flow multi-flow stateful 지원 권장 용도
pktgen (커널) ~10~40Mpps 낮음 매우 쉬움 △ (제한적) 단순 드라이버 처리량 테스트, 빠른 기준선
TRex ~200Gbps+ 높음 중간 실제 트래픽 시뮬레이션, NFV 검증
DPDK-pktgen ~100~200Gbps 중간 높음 DPDK 기반 드라이버 성능 측정
MoonGen ~100Gbps 매우 높음 높음 정밀 타임스탬프 지연 측정, 학술 연구
## pktgen (커널 내장): 가장 빠른 기준선 측정
modprobe pktgen
echo "add_device eth0@0" > /proc/net/pktgen/kpktgend_0
pgset() { echo "$1" > /proc/net/pktgen/eth0@0; }
pgset "count 10000000"
pgset "pkt_size 64"
pgset "dst_mac 00:11:22:33:44:55"
pgset "dst 192.168.1.1"
echo "start" > /proc/net/pktgen/pgctrl
cat /proc/net/pktgen/eth0@0 | grep -E 'pps|bps|errors'
벤치마크 파이프라인 구성 Traffic Generator (TRex / pktgen / MoonGen) CPU pinning: isolcpus=4-7 IRQ affinity: CPU 4 NIC: 100GbE port 0 TX rate 측정점 패킷 전송 latency probe DUT (Device Under Test) 커널 + NIC 드라이버 perf/bpftrace 계측 IRQ affinity: CPU 0-3 RX rate 측정점 반환/포워딩 Traffic Sink (또는 루프백) RX 확인 / 드롭 카운트 latency timestamp 비교 수신 확인점 측정 포인트 latency probe 패킷 흐름 * CPU 고정(isolcpus)과 IRQ affinity 설정은 재현성 확보의 핵심
그림 ndd-65. 벤치마크 파이프라인: Traffic Generator → DUT → Traffic Sink 구성 및 측정 포인트

드라이버별 코얼레싱 전략

인터럽트 코얼레싱은 지연 시간(latency)과 처리량(throughput) 사이의 근본적 트레이드오프입니다. 코얼레싱 간격이 길수록 인터럽트 오버헤드가 줄어 throughput이 향상되지만, 패킷 처리 지연이 증가합니다.

코얼레싱 — Latency vs Throughput 트레이드오프 코얼레싱 간격 (µs) → 성능 지표 0 8 20 50 100 250 500 Throughput Latency IRQ/s mlx5e (8µs) bnxt (16µs) ixgbe (20µs) ice (50µs) 범용 최적 구간 저지연 워크로드 고처리량 워크로드 ● Throughput (높을수록 좋음) ● Latency (낮을수록 좋음) - - IRQ/s (CPU 부하 지표)
그림 ndd-59. 코얼레싱 간격에 따른 Latency/Throughput/IRQ 트레이드오프 및 주요 드라이버 기본 설정 위치

주요 드라이버의 코얼레싱 파라미터를 비교합니다.

드라이버 Rx usecs Rx frames Tx usecs Tx frames Adaptive 모드 추천 워크로드
mlx5e 8 128 16 128 DIM + CQ 모더레이션 저지연/HPC
bnxt_en 16 64 16 64 DIM 범용
ixgbe 20 8 72 32 DIM 범용/서버
ice 50 128 50 128 DIM 고처리량/NFV
i40e 50 50 커널 DIM 고처리량
ena 20 64 ENA 자체 적응형 클라우드 범용
cxgb4 10 8 1 8 자체 타이머 RDMA/iSCSI
# 현재 코얼레싱 설정 확인
ethtool -c eth0

# 저지연 튜닝 (mlx5e 예시: 코얼레싱 최소화)
ethtool -C eth0 rx-usecs 0 tx-usecs 0 adaptive-rx off adaptive-tx off

# 고처리량 튜닝 (ice 예시: 코얼레싱 증가)
ethtool -C eth0 rx-usecs 100 tx-usecs 100 rx-frames 256 tx-frames 256

# Adaptive 코얼레싱 활성화 (대부분의 워크로드에 추천)
ethtool -C eth0 adaptive-rx on adaptive-tx on
워크로드별 추천 코얼레싱 설정:
  • 금융/HFT (초저지연): rx-usecs 0, adaptive off, busy-poll 병행
  • 웹 서버 (범용): adaptive-rx on, 기본값 사용
  • 스트리밍/CDN (고처리량): rx-usecs 100~250, rx-frames 256+
  • NFV/패킷 처리: rx-usecs 0, XDP + busy-poll 조합

코얼레싱과 XDP 상호작용

XDP를 사용할 때 코얼레싱 설정은 일반 스택 경로와 다른 영향을 미칩니다. 드라이버마다 HW 코얼레싱 지원 수준이 다르며, XDP 동작 모드에 따라 최적 설정이 달라집니다.

드라이버 코얼레싱 방식 rx-usecs 범위 rx-frames 지원 adaptive RX XDP와 상호작용
mlx5e HW 타이머 + DIM 0~8192 ✓ (DIM) XDP_DROP 시 rx-usecs=0 최적, XDP_REDIRECT는 8~16 권장
ice HW 타이머 + DIM 0~8160 ✓ (DIM) DIM이 XDP 배치 크기 자동 조정, 수동 설정 가능
r8169 고정 타이머만 고정값 코얼레싱 조정 불가, XDP 성능 제한 요인
atlantic adaptive (자체) 0~500 ✓ (자체) XDP 로드 시 adaptive 동작 유지, 개별 조정 지원
gve DQO adaptive 호스트 관리 ✓ (DQO) GCP 호스트가 코얼레싱 조정, 직접 제어 제한적
virtio_net 없음 (HW 없음) N/A HW 코얼레싱 없음; vhost 배치 크기로 간접 조정
hv_netvsc 호스트 관리 호스트 결정 △ (호스트) Hyper-V 호스트가 코얼레싱 결정, 게스트 조정 불가
XDP 동작 모드별 최적 코얼레싱 전략:
  • XDP_DROP: rx-usecs=0 + adaptive-rx off — 각 패킷을 즉시 처리하여 최소 지연
  • XDP_REDIRECT (devmap bulk): rx-usecs=8~16 — 배치 flush 크기와 정렬하여 처리량 최대화
  • XDP_PASS (스택 전달): adaptive 코얼레싱 유지 — 일반 스택 경로와 동일한 최적화
  • XDP_TX (동일 NIC 반환): RX/TX 코얼레싱 동시 조정 필요, 동일 값 설정 권장
## bpftrace: 코얼레싱 설정과 XDP 처리 지연 상관관계 추적
bpftrace -e '
kprobe:xdp_do_redirect {
  @xdp_start[tid] = nsecs;
}
kretprobe:xdp_do_redirect /@xdp_start[tid]/ {
  @xdp_lat_us = hist((nsecs - @xdp_start[tid]) / 1000);
  delete(@xdp_start[tid]);
}
kprobe:napi_poll {
  @napi_start[arg0] = nsecs;
}
kretprobe:napi_poll /@napi_start[arg0]/ {
  @napi_lat_us = hist((nsecs - @napi_start[arg0]) / 1000);
  delete(@napi_start[arg0]);
}
interval:s:10 {
  print("XDP redirect 지연:");
  print(@xdp_lat_us);
  print("NAPI poll 지연:");
  print(@napi_lat_us);
  exit();
}'

링 버퍼 크기 영향 분석

링 버퍼 크기는 버스트 트래픽 흡수 능력과 메모리 사용량, 그리고 캐시 효율성 사이의 균형을 결정합니다. 버퍼가 작으면 트래픽 버스트 시 패킷 손실이 발생하고, 너무 크면 bufferbloat로 인한 지연 증가와 메모리 낭비가 발생합니다.

드라이버 Rx 기본 Rx 최소 Rx 최대 Tx 기본 Tx 최소 Tx 최대 2의 거듭제곱 필수
ice 1024 64 8192 1024 64 8192
mlx5e 1024 64 8192 1024 64 8192
bnxt_en 512 32 32768 512 32 32768
ixgbe 512 64 4096 512 64 4096 ✓ (8의 배수)
ena 1024 256 16384 1024 256 16384
virtio_net 256 32 32768 256 32 32768
r8169 256 256 1024 256 256 1024
링 크기 장점 단점 추천 워크로드
64~256 최소 메모리, 캐시 적중률 최대 버스트 시 패킷 손실 위험 저지연/HFT, busy-poll 사용 시
512~1024 범용 균형, 적당한 버스트 흡수 일반 서버, 웹/앱 서비스
2048~4096 높은 버스트 흡수력 메모리 증가, 캐시 미스 증가 고속(25G+), 트래픽 변동 큰 환경
8192+ 극한 버스트 대응 bufferbloat 위험, 메모리 다량 소비 패킷 캡처, 특수 목적
# 현재 링 버퍼 크기 확인
ethtool -g eth0

# 링 버퍼 크기 조정 (온라인, 대부분 트래픽 순간 중단)
ethtool -G eth0 rx 2048 tx 2048

# 메모리 사용량 추정 (ring_size × descriptor_size × num_queues)
# 예: 2048 × 16B × 32큐 = 1MB (디스크립터만)
# 실제 버퍼 메모리: 2048 × 4KB × 32큐 = 256MB (page_pool 기준)

# 패킷 드롭 모니터링 (링 오버플로우 감지)
ethtool -S eth0 | grep -E 'rx_dropped|rx_no_buffer|rx_missed'

버퍼블로트 진단 워크플로우

버퍼블로트(bufferbloat)는 과도한 링 버퍼 또는 qdisc 큐로 인해 RTT가 폭발적으로 증가하는 현상입니다. 증상만 보면 단순 지연 증가처럼 보이지만, 원인은 링 레벨 또는 qdisc 레벨 중 어느 한 곳에 있습니다.

버퍼블로트 유형 원인 진단 도구 지표
링 레벨 TX ring 가득 참 + TX 소비 느림 ethtool -S, bpftrace tx_queue_stopped, sojourn time >1ms
qdisc 레벨 pfifo/bfifo 큐 과다 적재 tc -s qdisc dropped 증가, backlog 상시 nonzero
BQL 미적용 Byte Queue Limits 비활성화 /sys/class/net/eth0/queues/tx-N/byte_queue_limits/ limit 값 과다 또는 0
애플리케이션 send() 버스트 + SO_SNDBUF 과대 ss -ti RTT 폭증, snd_wnd 대비 snd_cwnd 불균형

아래는 단계별 버퍼블로트 진단 워크플로우입니다. 위에서 아래로 순서대로 점검합니다.

## Step 1: 링 충전율 확인
# TX ring이 가득 찬 횟수 (링 레벨 bufferbloat 지표)
ethtool -S eth0 | grep -E 'tx_queue_stopped|tx_busy|tx_restart'

## Step 2: BQL 상태 확인
# limit: 현재 허용 바이트, limit_max: 최대 허용
cat /sys/class/net/eth0/queues/tx-0/byte_queue_limits/limit
cat /sys/class/net/eth0/queues/tx-0/byte_queue_limits/limit_max
# BQL 수동 제한 (bufferbloat 완화)
echo 10240 > /sys/class/net/eth0/queues/tx-0/byte_queue_limits/limit_max

## Step 3: qdisc 큐 상태 확인
tc -s qdisc show dev eth0
# 출력에서 backlog nonzero 지속 + dropped 증가 = qdisc bufferbloat
# 해결: fq_codel 또는 cake qdisc로 교체
tc qdisc replace dev eth0 root fq_codel

## Step 4: 애플리케이션 RTT 분석
# ss -ti: srtt (smoothed RTT), rttvar (RTT variance)
ss -ti dst 192.168.1.1 | grep -E 'rtt|cwnd|wscale'
## bpftrace: TX ring sojourn time (패킷이 TX ring에서 보내는 시간)
# 높은 sojourn time = TX ring에서 드라이버가 패킷을 늦게 소비
bpftrace -e '
kprobe:dev_queue_xmit {
  @enqueue[skbuff->hash] = nsecs;
}
kprobe:dev_hard_start_xmit /@enqueue[skbuff->hash]/ {
  $sojourn = nsecs - @enqueue[skbuff->hash];
  @sojourn_us = hist($sojourn / 1000);
  delete(@enqueue[skbuff->hash]);
}
interval:s:10 {
  print("TX ring sojourn time (us):");
  print(@sojourn_us);
  exit();
}'
드라이버 권장 RX 링 크기 권장 TX 링 크기 워크로드 이유
mlx5e 1024~2048 1024 고처리량 서버 HW rate limiting 지원으로 큰 링도 bufferbloat 없음
mlx5e 256~512 512 HFT/저지연 작은 링으로 캐시 효율 극대화, busy-poll 병행
ice 1024 1024 범용/NFV DIM adaptive가 동적 조정, 기본값 유지 권장
r8169 256 256 데스크톱/소규모 백프레셔 없음, 큰 링은 bufferbloat 직결
bnxt_en 1024~2048 512 고처리량 HW CQ 분리 구조로 큰 링 허용, TX는 작게 유지
virtio_net 512 256 VM 범용 vhost 배치와 정렬, 큰 링은 VM 간 지연 증가
ena 1024 1024 클라우드 서버 AWS ENA 드라이버 자체 adaptive, 기본값 최적화됨
버퍼블로트 진단 결정 트리 높은 RTT / 지연 급증 감지 ethtool -S: tx_queue_stopped > 0? Yes 링 레벨 bufferbloat TX 링 축소 또는 BQL limit_max 감소 No tc -s qdisc: backlog 지속 증가? Yes qdisc 레벨 bufferbloat fq_codel / cake qdisc로 교체 No BQL limit 값이 과다하게 큰가? Yes BQL 미적용 문제 limit_max 감소 (10240~65536 bytes) No 애플리케이션 수준 ss -ti로 RTT/cwnd 분석 SO_SNDBUF 축소 검토 진단 도구 요약 링 레벨: ethtool -S | grep tx_queue qdisc 레벨: tc -s qdisc show dev eth0 BQL: /sys/.../byte_queue_limits/ 애플리케이션: ss -ti dst <peer> 자동 완화: tc qdisc add fq_codel 또는 cake (CAKE AQM) 추적: bpftrace TX sojourn time ndd-66 레시피 참조
그림 ndd-66. 버퍼블로트 진단 결정 트리: 링 레벨 → BQL → qdisc → 애플리케이션 순서로 점검

NUMA 친화성

멀티소켓 시스템에서 NIC의 NUMA 노드와 CPU/메모리 배치가 일치하지 않으면 크로스-노드 메모리 접근으로 인한 심각한 성능 저하가 발생합니다. 올바른 NUMA 배치는 10~30%의 성능 차이를 만들어냅니다.

2-소켓 시스템 NUMA 큐-CPU-메모리 최적 배치 NUMA Node 0 (Socket 0) CPU 코어 C0 C1 C2 C3 ... C15 로컬 메모리 (DDR4) L3 캐시 (공유) NIC (PCIe) NUMA Node 0 소속 IRQ→큐 매핑 (최적): IRQ0→Q0→C0 | IRQ1→Q1→C1 | IRQ2→Q2→C2 | IRQ3→Q3→C3 ... 로컬 DMA (최적) ✓ 최적: 동일 NUMA 노드 배치 NUMA Node 1 (Socket 1) CPU 코어 C16 C17 C18 C19 ... C31 로컬 메모리 (DDR4) L3 캐시 (공유) 크로스-NUMA DMA (비최적, +40~80ns) 잘못된 IRQ→큐 매핑: IRQ0→Q0→C16 | IRQ1→Q1→C17 (원격 노드 CPU!) ✗ 비최적: 크로스-NUMA 배치 QPI/UPI
그림 ndd-60. 2-소켓 NUMA 시스템에서의 큐-CPU-메모리 배치 최적화와 크로스-NUMA 영향
# NIC의 NUMA 노드 확인
cat /sys/class/net/eth0/device/numa_node

# CPU별 NUMA 노드 확인
lscpu | grep -i numa
numactl --hardware

# IRQ affinity를 NIC의 NUMA 노드 CPU에 고정
# 예: NIC가 NUMA 0 (CPU 0-15)에 있을 때
for irq in $(grep eth0 /proc/interrupts | awk '{print $1}' | tr -d ':'); do
    echo 0000ffff > /proc/irq/$irq/smp_affinity
done

# irqbalance에서 NUMA 인식 활성화
IRQBALANCE_ARGS="--hintpolicy=exact"

# 애플리케이션의 NUMA 바인딩
numactl --cpunodebind=0 --membind=0 ./my_server

# NUMA 메모리 접근 통계 확인
numastat -p $(pgrep my_server)
구성 상대 성능 (PPS) 평균 지연 비고
동일 NUMA (최적) 100% 기준값 NIC/CPU/메모리 동일 노드
CPU만 원격 NUMA 70~85% +15~30% DMA는 로컬, 처리는 원격
메모리만 원격 NUMA 75~90% +10~20% page_pool 할당이 원격
전체 크로스-NUMA 60~75% +30~50% 모든 접근이 QPI/UPI 경유

XDP 성능 비교

XDP(eXpress Data Path)는 드라이버 레벨에서 패킷을 처리하여 커널 네트워크 스택 오버헤드를 완전히 제거합니다. 드라이버의 XDP 구현 품질과 하드웨어 능력에 따라 성능 차이가 현저합니다.

XDP_DROP 성능 비교 (단일 코어, 64B 패킷) 100GbE NIC 기준, 상대적 비교 (Mpps) Mpps (백만 패킷/초) → 0 5 10 15 20 25 mlx5e ~24 ice ~22 bnxt_en ~18 i40e ~14 ixgbe ~12 ena ~5 virtio_net ~3 atlantic ~8 mvpp2 ~4 generic XDP ~2 ※ 실측값은 CPU 클럭, 메모리 대역폭, 펌웨어 버전에 따라 변동됩니다
그림 ndd-61. 드라이버별 XDP_DROP PPS 비교 (단일 코어, 64B 패킷, 100GbE 기준 상대 비교)
드라이버 XDP Native XDP Redirect XDP Multi-buf XDP HW Offload XDP 연결 방식 XDP 메타데이터
mlx5e bpf_prog_attach ✓ (rx_hash, mark)
ice ndo_bpf ✓ (rx_hash)
bnxt_en ndo_bpf
i40e ndo_bpf
ixgbe ndo_bpf
ena ndo_bpf
virtio_net ndo_bpf
atlantic ndo_bpf
mvpp2 ndo_bpf
# XDP 프로그램 로드 (Native 모드)
ip link set dev eth0 xdp obj xdp_drop.o sec xdp

# XDP 프로그램 상태 확인
ip link show dev eth0 | grep xdp
bpftool net show

# XDP 성능 벤치마크 (xdp-tools 활용)
xdp-bench drop eth0

# XDP redirect 벤치마크 (인터페이스 간 포워딩)
xdp-bench redirect eth0 eth1

# XDP 통계 확인
bpftool prog show
ethtool -S eth0 | grep xdp

# Generic XDP fallback (Native 미지원 드라이버)
ip link set dev eth0 xdpgeneric obj xdp_prog.o sec xdp
Generic vs Native XDP 성능 차이: Generic XDP는 netif_receive_skb() 이후 SKB가 이미 생성된 상태에서 실행되므로 Native XDP 대비 5~10배 느립니다. 성능이 중요한 환경에서는 반드시 Native XDP를 지원하는 드라이버를 선택해야 합니다.

XDP REDIRECT: copy vs reference 경로

XDP_REDIRECT 성능은 드라이버가 page_pool shared reference를 사용하는지(zero-copy), 아니면 패킷을 메모리 복사하는지(copy)에 따라 크게 달라집니다. 참조 방식은 복사 오버헤드가 없지만 page lifetime 관리가 복잡하고, 복사 방식은 단순하지만 64B 패킷 기준 20~40ns의 추가 지연이 발생합니다.

드라이버 zero-copy XDP_TX page_pool 사용 REDIRECT 모드 memory overhead 비고
mlx5e reference 낮음 (ref count만) page_pool ptr ring, XDP_TX 동일 큐 최적화
ice reference 낮음 devmap bulk flush 지원
bnxt_en reference 낮음 page_pool recycle 경로 최적화
i40e copy 중간 page_pool 부분 지원, REDIRECT는 복사 경로
ixgbe copy 높음 레거시 alloc 방식, 모든 XDP action에 복사
virtio_net copy 높음 vhost 구조상 zero-copy 불가, 항상 복사
ena copy 중간~높음 AWS ENA v2+ 부분 page_pool 지원
XDP_TX 경로: same-queue vs cross-queue 차이:
  • same-queue TX: mlx5e/ice는 RX 큐와 동일한 TX 큐에 XDP_TX를 처리하여 큐 락 오버헤드 없음
  • cross-queue TX: RX와 TX 큐가 다를 경우 스핀락 경합 발생 — IRQ affinity 설정 시 같은 큐 쌍 보장 필요
  • devmap bulk flush: XDP_REDIRECT는 bpf_redirect_map() + devmap을 사용할 때 배치 크기(32~64)로 flush하여 NIC 제출 횟수 감소

XDP 멀티버퍼 프로그래밍

점보 프레임(Jumbo frame) 또는 TSO/GRO 세그먼트를 XDP로 처리하려면 XDP multi-buffer 지원이 필요합니다. 단일 xdp_buff가 여러 page fragment를 가지며, BPF 프로그램에서 명시적으로 순회해야 합니다.

/* xdp_buff vs xdp_frame 구조 핵심 필드 */
struct xdp_buff {
    void        *data;          /* 현재 패킷 데이터 포인터 */
    void        *data_end;      /* 데이터 끝 포인터 */
    void        *data_meta;     /* 메타데이터 영역 */
    void        *data_hard_start;
    struct xdp_rxq_info *rxq;
    struct xdp_txq_info *txq;
    u32          flags;         /* XDP_FLAGS_HAS_FRAGS 등 */
};

/* 멀티버퍼 여부 확인 */
if (xdp_buff_has_frags(xdp)) {
    struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
    u32 total_len = xdp_get_buff_len(xdp); /* 전체 길이 */

    /* fragment 순회 */
    for (int i = 0; i < sinfo->nr_frags; i++) {
        skb_frag_t *frag = &sinfo->frags[i];
        void *frag_data = skb_frag_address(frag);
        u32  frag_size  = skb_frag_size(frag);
        /* frag_data[0..frag_size-1] 처리 */
    }
}
/* BPF 프로그램에서 XDP multi-buffer fragment 순회 예시 */
SEC("xdp")
int xdp_multibuf_example(struct xdp_md *ctx)
{
    void *data     = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;

    /* 헤더 파싱 (첫 번째 버퍼) */
    struct ethhdr *eth = data;
    if ((void *)(eth + 1) > data_end)
        return XDP_DROP;

    /* 멀티버퍼: bpf_xdp_get_buff_len()으로 전체 길이 확인 */
    __u32 total = bpf_xdp_get_buff_len(ctx);
    if (total > 65535)
        return XDP_DROP; /* 비정상 크기 드롭 */

    /* fragment 접근: bpf_xdp_load_bytes() 사용 (오프셋 기반) */
    __u8 buf[16];
    if (bpf_xdp_load_bytes(ctx, sizeof(*eth) + 20, buf, sizeof(buf)) < 0)
        return XDP_PASS;

    return XDP_PASS;
}
XDP 멀티버퍼 성능 주의사항:
  • fragment 순회 비용: nr_frags가 클수록 루프 반복 증가 — 일반적으로 nr_frags ≤ 5 권장
  • bpf_xdp_load_bytes()는 경계 검사를 포함하므로 단순 포인터 접근보다 느림
  • 멀티버퍼 XDP_REDIRECT는 각 fragment의 page_pool ref를 개별 처리 — redirect 비용이 단일 버퍼 대비 nr_frags배
  • 드라이버 지원 여부 확인: ethtool -k eth0 | grep scatter
XDP_REDIRECT: copy 경로 vs zero-copy reference 경로 Copy 경로 (ixgbe, virtio_net, i40e) Zero-copy Reference 경로 (mlx5e, ice, bnxt) Driver kmalloc 패킷 수신 버퍼 할당 XDP BPF 실행 XDP_REDIRECT 결정 memcpy() 새 버퍼로 복사 (+20~40ns) 목적지 NIC 전송 복사된 버퍼 사용 총 오버헤드: kmalloc + memcpy + kfree = ~50~80ns/pkt page_pool 할당 pre-allocated page 재사용 XDP BPF 실행 XDP_REDIRECT 결정 page ref++ (no copy) 포인터만 전달 (~2ns) 목적지 NIC 전송 동일 page DMA 총 오버헤드: ref_count 조작 + DMA 재설정 = ~5~10ns/pkt devmap Bulk Flush 최적화 (양쪽 경로 공통) XDP_REDIRECT + BPF_MAP_TYPE_DEVMAP: 32~64개 패킷을 배치로 묶어 단일 ndo_xdp_xmit() 호출 → NIC 제출 시스템콜 오버헤드 대폭 감소 | mlx5e: bulk=32, ice: bulk=64 copy 경로 zero-copy reference 경로
그림 ndd-67. XDP_REDIRECT copy 경로 vs zero-copy reference 경로 비교 및 devmap bulk flush 최적화

드라이버별 커널 트레이싱 레시피 통합

아래 표는 주요 드라이버별로 즉시 사용 가능한 ftrace, bpftrace, perf 원라이너를 정리한 것입니다. 각 원라이너는 해당 드라이버의 핵심 성능 함수를 대상으로 하며, 커널 버전 6.x 기준입니다.

드라이버 ftrace 원라이너 bpftrace 원라이너 perf 원라이너
mlx5e echo mlx5e_napi_poll > set_graph_function bpftrace -e 'kprobe:mlx5e_napi_poll{@[tid]=nsecs} kretprobe:mlx5e_napi_poll/@[tid]/{@us=hist((nsecs-@[tid])/1000);delete(@[tid])} i:s:5{print(@us);exit()}' perf record -e mlx5e:* -C 2 sleep 5; perf report
ice echo ice_napi_poll > set_graph_function bpftrace -e 'kprobe:ice_napi_poll{@s[arg0]=nsecs} kretprobe:ice_napi_poll/@s[arg0]/{@=hist((nsecs-@s[arg0])/1000);delete(@s[arg0])} i:s:5{print(@);exit()}' perf stat -e 'ice:*' -C 2 sleep 5
bnxt_en echo bnxt_poll > set_graph_function bpftrace -e 'kprobe:bnxt_poll{@s[arg0]=nsecs} kretprobe:bnxt_poll/@s[arg0]/{@=hist((nsecs-@s[arg0])/1000);delete(@s[arg0])} i:s:5{print(@);exit()}' perf record -g -e cycles:u -C 2 -- sleep 5; perf report -F sym | grep bnxt
r8169 echo rtl8169_poll > set_graph_function bpftrace -e 'kprobe:rtl8169_poll{@s[arg0]=nsecs} kretprobe:rtl8169_poll/@s[arg0]/{@=hist((nsecs-@s[arg0])/1000);delete(@s[arg0])} i:s:5{print(@);exit()}' perf stat -e cache-misses,cycles -C 2 sleep 5
atlantic echo aq_napi_poll > set_graph_function bpftrace -e 'kprobe:aq_napi_poll{@s[arg0]=nsecs} kretprobe:aq_napi_poll/@s[arg0]/{@=hist((nsecs-@s[arg0])/1000);delete(@s[arg0])} i:s:5{print(@);exit()}' perf record -e cycles:u -g -C 2 sleep 5; perf report | grep aq_
ena echo ena_io_poll > set_graph_function bpftrace -e 'kprobe:ena_io_poll{@s[arg0]=nsecs} kretprobe:ena_io_poll/@s[arg0]/{@=hist((nsecs-@s[arg0])/1000);delete(@s[arg0])} i:s:5{print(@);exit()}' perf stat -e net:napi_poll -C 2 sleep 5
gve echo gve_rx_poll > set_graph_function bpftrace -e 'kprobe:gve_rx_poll{@s[arg0]=nsecs} kretprobe:gve_rx_poll/@s[arg0]/{@=hist((nsecs-@s[arg0])/1000);delete(@s[arg0])} i:s:5{print(@);exit()}' perf record -e cycles -g -C 2 sleep 5; perf report | grep gve
virtio_net echo virtnet_poll > set_graph_function bpftrace -e 'kprobe:virtnet_poll{@s[arg0]=nsecs} kretprobe:virtnet_poll/@s[arg0]/{@=hist((nsecs-@s[arg0])/1000);delete(@s[arg0])} i:s:5{print(@);exit()}' perf stat -e 'virtio_net:*' -C 2 sleep 5 2>/dev/null || perf stat -e net:napi_poll -C 2 sleep 5
ftrace 원라이너 공통 설정 절차:
cd /sys/kernel/debug/tracing
echo 0 > tracing_on
echo function_graph > current_tracer
echo '드라이버_함수명' > set_graph_function  # 위 표의 ftrace 원라이너 적용
echo 1 > tracing_on
sleep 1
echo 0 > tracing_on
cat trace | head -100

AF_XDP Zero-Copy 지원 매트릭스

AF_XDP Zero-Copy는 커널 버퍼 복사 없이 사용자 공간에서 직접 패킷을 송수신하는 최고 성능 경로입니다. 모든 드라이버가 Zero-Copy를 지원하는 것은 아니며, 지원하더라도 구현 성숙도에 차이가 있습니다.

드라이버 AF_XDP ZC ZC RX ZC TX Multi-queue ZC ZC 성능 (RX, 64B) Copy 모드 대비 향상
ice ~20 Mpps 2~3x
mlx5e ~22 Mpps 2~3x
bnxt_en ~15 Mpps 2x
i40e ~12 Mpps 2x
ixgbe ~8 Mpps 1.5~2x
igc ~1.4 Mpps 1.5x
stmmac 하드웨어 의존 1.5~2x
ena Copy만 가능
virtio_net Copy만 가능
gve Copy만 가능
# AF_XDP Zero-Copy 소켓 생성 확인
# xdpsock 도구로 ZC 모드 테스트
xdpsock -i eth0 -r -z  # -z: zero-copy 모드

# ZC 지원 여부 확인 (에러 없이 바인드되면 지원)
xdpsock -i eth0 -r -z -q 0

# Copy 모드 (fallback, 모든 드라이버 지원)
xdpsock -i eth0 -r -c  # -c: copy 모드

# AF_XDP 큐 할당 확인
ethtool -N eth0 rx-flow-hash udp4 sdfn
ethtool -X eth0 equal 4  # RSS 큐 수 조정
AF_XDP Zero-Copy 요구사항: Zero-Copy를 사용하려면 (1) 드라이버 ZC 지원, (2) XDP 프로그램이 해당 인터페이스에 로드되어 있어야 하며, (3) UMEM 영역이 hugepage로 할당되면 성능이 추가 향상됩니다. mlx5eice는 가장 성숙한 ZC 구현을 제공합니다.

패킷 처리 한계

네트워크 드라이버의 이론적 최대 PPS(Packets Per Second)는 링크 속도와 패킷 크기에 의해 결정되지만, 실제 달성 가능한 PPS는 드라이버 효율성, CPU 클럭, 메모리 대역폭에 의해 제한됩니다.

패킷 크기별 이론적 최대 PPS vs 실측 PPS 링크 속도별 비교 (Ethernet 프레이밍 포함) 패킷 크기 10 GbE 25 GbE 40 GbE 100 GbE 400 GbE 64B (최소 프레임) 14.88 Mpps 실측: ~12 37.20 Mpps 실측: ~25 59.52 Mpps 실측: ~35 148.81 Mpps 실측: ~80 595.24 Mpps 실측: ~150 512B 2.35 Mpps 5.87 Mpps 9.39 Mpps 23.49 Mpps 93.98 Mpps 1518B (MTU 1500) 0.81 Mpps 실측: ~0.8 2.03 Mpps 실측: ~2.0 3.25 Mpps 실측: ~3.2 8.12 Mpps 실측: ~8.0 32.47 Mpps 실측: ~30 핵심 병목 분석 64B: CPU cycles/packet이 병목 (작은 패킷 → 높은 인터럽트 빈도, 디스크립터 처리 오버헤드) 1518B: 링크 대역폭이 병목 (큰 패킷 → 이론치에 근접, CPU 오버헤드 상대적으로 미미) 이론치 대비 실측 달성률 64B (100G) ~54% (CPU 병목) 512B (100G) ~80% 1518B (100G) ~98% (링크 병목)
그림 ndd-62. 패킷 크기별 이론적 최대 PPS vs 실측 달성률 (속도별 비교)

병목 지점을 식별하는 방법론입니다.

병목 유형 증상 진단 방법 해결 전략
CPU 병목 softirq CPU 100%, PPS 정체 mpstat -P ALL 1, perf top XDP, 큐 수 증가, busy-poll
메모리 대역폭 NUMA 미스, LLC 미스 증가 perf stat -e LLC-load-misses NUMA 배치 최적화, page_pool
PCIe 대역폭 양방향 100G+ 시 포화 lspci -vvv (LinkSta) PCIe Gen4 x16 이상 슬롯 사용
링 버퍼 부족 rx_no_buffer_count 증가 ethtool -S 드롭 카운터 링 크기 증가, NAPI budget 조정
인터럽트 폭풍 IRQ 처리 CPU 과점유 /proc/interrupts 코얼레싱 증가, adaptive 활성화
드라이버 락 경합 lock_stat에서 spinlock 경합 perf lock record/report per-CPU 큐 분리, lockless 디자인
# 종합 성능 진단 스크립트

# 1. CPU softirq 부하 확인
mpstat -P ALL 1 5 | grep -E 'CPU|all|Average'

# 2. 인터럽트 분포 확인
watch -n 1 'grep eth0 /proc/interrupts'

# 3. 드라이버 통계 (드롭/에러 카운터)
ethtool -S eth0 | grep -E 'drop|error|miss|no_buffer|overflow'

# 4. PCIe 대역폭 상태
lspci -s $(ethtool -i eth0 | grep bus-info | awk '{print $2}') -vvv | grep -E 'LnkSta:|Width|Speed'

# 5. perf로 핫스팟 분석
perf top -g --no-children -e cycles:ppp -p $(pgrep -d, ksoftirqd)

CPU 활용 효율 (cycles/packet)

패킷당 CPU 사이클 소비량은 드라이버 효율성의 핵심 지표입니다. 동일한 CPU에서 더 적은 사이클로 패킷을 처리할수록 더 높은 PPS를 달성할 수 있습니다. 이 값은 드라이버의 데이터 경로 최적화 수준, DMA 매핑 전략, 캐시 친화성에 의해 결정됩니다.

드라이버 일반 RX 경로 (cycles/pkt) XDP_DROP (cycles/pkt) XDP_TX (cycles/pkt) 주요 최적화
mlx5e ~800 ~100 ~180 MPWQE, 인라인 WQE, CQE 압축
ice ~850 ~110 ~200 Flex descriptor, page_pool, DIM
bnxt_en ~900 ~130 ~220 TPA (GRO HW), page_pool
ixgbe ~1000 ~150 ~250 페이지 분할, bulk alloc
i40e ~950 ~140 ~230 DIM, 동적 ITR
ena ~1200 ~400 ~500 LLQ (Low Latency Queue)
virtio_net ~1500 ~500 ~700 mergeable buffer, page_pool
vmxnet3 ~1800 ~600 N/A 제한적 (가상화 오버헤드)
r8169 ~2000 N/A N/A 기본적 (1GbE 전용)
cycles/packet 측정 방법: perf stat -e cycles,instructions -C <cpu> -- sleep 1로 해당 CPU의 사이클 수를 측정하고, 동시에 ethtool -S에서 패킷 카운터 변화를 관찰하여 산출합니다. 정확한 측정을 위해 단일 큐/단일 CPU로 고정하고, 다른 워크로드를 제거해야 합니다.

CPU 효율성에 영향을 미치는 핵심 요소와 드라이버별 최적화 전략입니다.

최적화 기법 사이클 절감 효과 적용 드라이버 원리
page_pool 재활용 ~150 cycles mlx5e, ice, bnxt 등 DMA map/unmap 제거, 페이지 할당 회피
NAPI bulk alloc ~80 cycles 대부분 최신 드라이버 배치 할당으로 per-packet 오버헤드 분산
XDP 바이패스 ~600 cycles XDP 지원 드라이버 SKB 생성 및 커널 스택 완전 우회
CQE 압축 ~50 cycles mlx5e 여러 CQE를 하나의 압축 CQE로 처리
Inline WQE ~30 cycles (TX) mlx5e 소형 패킷 헤더를 WQE에 인라인 삽입
DIM 적응형 코얼레싱 가변 ice, mlx5e, bnxt 워크로드에 따라 인터럽트 빈도 자동 조정
GRO HW (TPA) ~200 cycles bnxt_en, mlx5e 하드웨어에서 TCP 세그먼트 병합
Prefetch 힌트 ~30 cycles ixgbe, i40e 다음 디스크립터/데이터를 미리 캐시 로드
# CPU 사이클 효율 측정 (단일 코어 고정)

# 1. IRQ를 특정 CPU에 고정
echo 1 > /proc/irq/48/smp_affinity_list

# 2. 해당 CPU의 사이클 측정
perf stat -e cycles,instructions,cache-misses,LLC-load-misses -C 1 -- sleep 10

# 3. 패킷 카운터 확인 (10초 전후 차이 계산)
ethtool -S eth0 | grep rx_packets

# 4. cycles/packet 계산
# cycles_per_pkt = total_cycles / (rx_packets_after - rx_packets_before)

# 5. 함수별 사이클 분포 (드라이버 핫스팟 분석)
perf record -g -C 1 -- sleep 10
perf report --no-children --sort=dso,symbol
드라이버 유형별 최적화 우선순위:
  • 베어메탈 고성능 NIC (mlx5e, ice): XDP → page_pool → NUMA 배치 → 코얼레싱 순으로 최적화
  • 클라우드 가상 NIC (ena, gve): 큐 수 최적화 → 링 버퍼 크기 → 코얼레싱 순으로 최적화 (드라이버 내부 변경 불가)
  • 가상화 환경 (virtio_net): vhost-net/vDPA 활성화 → 멀티큐 → mergeable buffer → busy-poll 순으로 최적화
  • 레거시/데스크톱 (r8169): 기본 설정으로 충분, 특별한 튜닝 불필요 (1GbE 대역폭이 병목)

DPDK PMD 관점의 NIC 드라이버 분석

지금까지 살펴본 커널 네트워크 드라이버(net_device)는 리눅스 커널 네트워크 스택 내에서 동작한다. 그러나 통신사 코어, NFV, 고빈도 거래(HFT) 등 극한의 패킷 처리 성능이 요구되는 환경에서는 커널 스택을 완전히 우회하는 DPDK(Data Plane Development Kit)가 사실상 표준이다. DPDK의 핵심은 PMD(Poll Mode Driver) — 커널 드라이버 대신 유저스페이스에서 NIC 하드웨어를 직접 제어하는 드라이버다. 이 섹션에서는 각 NIC 드라이버의 DPDK PMD 관점 차이, 아키텍처, 성능 비교를 분석한다.

커널 드라이버 vs DPDK PMD 아키텍처

커널 네트워크 스택에서 패킷이 애플리케이션에 도달하려면 socket → TCP/IP → netfilter → net_device → NIC 경로를 거치며, 매 패킷마다 컨텍스트 스위치, 인터럽트 처리, sk_buff 할당/해제 등 상당한 오버헤드가 발생한다. DPDK PMD는 이 경로를 전부 제거하고 App → rte_eth_rx/tx_burst() → PMD → NIC로 직접 접근한다.

커널 네트워크 스택 vs DPDK PMD 데이터 경로 커널 네트워크 스택 경로 Application (socket) System Call (syscall) TCP/IP Stack netfilter / tc net_device (NAPI) 커널 드라이버 (IRQ) NIC Hardware 7단계, 인터럽트+컨텍스트 스위치 vs DPDK PMD 경로 Application (rte_mbuf) rte_eth_rx/tx_burst() PMD (polling, no IRQ) NIC Hardware 3단계, 폴링 전용, zero-copy 커널 우회! No IRQ!

UIO vs VFIO-pci 바인딩 메커니즘

DPDK PMD가 NIC 하드웨어에 직접 접근하려면 기존 커널 드라이버에서 디바이스를 분리(unbind)하고 DPDK 호환 드라이버에 바인딩해야 한다. 두 가지 주요 메커니즘이 있다.

항목igb_uio (레거시)vfio-pci (권장)
IOMMU 보호없음 — DMA가 전체 물리 메모리 접근 가능있음 — IOMMU가 DMA 범위 제한
보안 수준낮음 — 악성 PMD가 커널 메모리 손상 가능높음 — HW 수준 격리
권한 요구root 필수non-root 가능 (vfio 그룹 권한)
멀티 디바이스IOMMU 그룹 무시 — 격리 불가IOMMU 그룹 단위 관리
인터럽트 지원기본 INTX/MSIMSI-X, eventfd 기반
상태DPDK에서 deprecated, 제거 예정기본 권장, 프로덕션 표준

디바이스 바인딩 워크플로우dpdk-devbind.py를 사용한 전형적인 바인딩 절차:

# 1. 현재 NIC 상태 확인
dpdk-devbind.py --status

# 출력 예시:
# 0000:03:00.0 'Ethernet Controller E810-C' if=ens3f0 drv=ice unused=vfio-pci
# 0000:03:00.1 'Ethernet Controller E810-C' if=ens3f1 drv=ice unused=vfio-pci

# 2. vfio-pci 모듈 로드
modprobe vfio-pci

# 3. 커널 드라이버에서 분리 후 vfio-pci에 바인딩
dpdk-devbind.py --bind=vfio-pci 0000:03:00.0

# 4. 바인딩 확인
dpdk-devbind.py --status
# 0000:03:00.0 'Ethernet Controller E810-C' drv=vfio-pci unused=ice

# 5. 원복 (운영 중 필요 시)
dpdk-devbind.py --bind=ice 0000:03:00.0

Hugepage 설정

DPDK는 hugepage를 통해 TLB 미스를 최소화하고 대용량 연속 메모리를 확보한다. NUMA 토폴로지를 반드시 고려해야 한다.

# 1G hugepage (권장 — TLB 엔트리 절약, 고성능)
# 커널 부팅 파라미터에 추가:
# default_hugepagesz=1G hugepagesz=1G hugepages=8

# 2M hugepage (기본, 런타임 할당 가능)
echo 2048 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

# NUMA 노드별 할당 (NIC과 같은 NUMA 노드에 배치)
echo 1024 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
echo 1024 > /sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages

# hugetlbfs 마운트
mkdir -p /dev/hugepages
mount -t hugetlbfs nodev /dev/hugepages

# NIC의 NUMA 노드 확인
cat /sys/bus/pci/devices/0000:03:00.0/numa_node
VFIO vs UIO 보안 차이: igb_uio는 IOMMU 보호가 없어 PMD 코드의 버그나 공격으로 인해 DMA가 커널 메모리를 직접 읽고 쓸 수 있다. 프로덕션 환경에서는 반드시 vfio-pci를 사용하고, BIOS에서 VT-d(Intel) 또는 AMD-Vi를 활성화해야 한다. 커널 부팅 파라미터에 intel_iommu=on iommu=pt를 추가하면 IOMMU passthrough 모드로 DPDK 최적 성능을 얻을 수 있다.

Bifurcated 드라이버 모델

전통적인 DPDK 사용 방식은 NIC을 커널 드라이버에서 분리하여 DPDK에 전용으로 할당하는 것이다. 그러나 이 방식은 ethtool, ip link, devlink 등 커널 관리 도구를 사용할 수 없고, 디바이스가 커널에서 완전히 사라진다는 운영상의 큰 단점이 있다.

Mellanox/NVIDIA mlx5는 이 문제를 근본적으로 해결하는 bifurcated 드라이버 모델을 제공한다. 커널 mlx5_core 드라이버가 NIC에 바인딩된 상태를 유지하면서, DPDK net_mlx5 PMD가 동시에 같은 NIC의 데이터 경로에 접근한다.

전통적 바인딩 vs Bifurcated 드라이버 모델 전통적 바인딩 (ice, ixgbe 등) DPDK App ethtool ✗ ip ✗ PMD (net_ice) vfio-pci 커널 드라이버 언바인드됨 1 커널 드라이버 unbind 2 vfio-pci bind 3 DPDK 앱 실행 NIC Hardware Bifurcated 모델 (mlx5) DPDK App ethtool ✓ ip ✓ PMD (net_mlx5) mlx5_core (커널) 디바이스 언바인드 불필요 커널 관리 도구 계속 사용 가능 RDMA + DPDK 동시 사용 SR-IOV VF도 bifurcated 지원 NIC Hardware (동시 접근)
항목전통적 바인딩 (vfio-pci)Bifurcated 모델 (mlx5)
디바이스 바인딩커널 드라이버 unbind → vfio-pci bind 필수mlx5_core 유지, 추가 바인딩 불필요
커널 관리 도구사용 불가 (ethtool, ip, devlink)정상 사용 가능
RDMA 공존불가 — 커널 드라이버 언바인드됨가능 — mlx5_ib 동시 동작
SR-IOV 관리VF 생성 전 PF unbind 필요 (복잡)PF/VF 모두 bifurcated 동작
Live Migration제한적 (vfio 기반 migration)mlx5_vdpa 경유 live migration 지원
FW 업데이트DPDK 앱 중단 → 커널 드라이버 복원 필요devlink fw 명령으로 즉시 가능
모니터링DPDK telemetry API만 가능ethtool -S + DPDK telemetry 모두 가능
운영 복잡도높음 — 바인딩 스크립트, 복구 절차 필요낮음 — 표준 커널 운영 워크플로우 유지
Bifurcated 모델이 프로덕션에서 선호되는 이유: 통신사/클라우드 운영 환경에서는 DPDK 성능뿐 아니라 모니터링(ethtool -S), FW 관리(devlink), 장애 복구, live migration이 동시에 필요하다. Bifurcated 모델은 이 모든 요구를 충족하면서 DPDK 데이터 경로 성능은 동일하게 유지한다. 이것이 mlx5 NIC이 DPDK 환경에서 사실상 1순위로 선택되는 핵심 이유다.

mlx5 testpmd 실행 — 디바이스 언바인드 없이 바로 DPDK 앱을 실행할 수 있다:

# mlx5 bifurcated: 커널 드라이버 유지한 채 바로 testpmd 실행
# (dpdk-devbind.py --bind 불필요!)
dpdk-testpmd -l 0-3 -n 4 \
  -a 0000:03:00.0 \
  -- -i --nb-cores=2 --rxq=2 --txq=2

# 동시에 다른 터미널에서 커널 관리 도구 사용 가능
ethtool -S ens3f0 | grep rx_packets
devlink dev info pci/0000:03:00.0
ip link show ens3f0

# ice (전통적 바인딩)와 비교 — 반드시 unbind 필요
dpdk-devbind.py --bind=vfio-pci 0000:04:00.0
dpdk-testpmd -l 4-7 -n 4 \
  -a 0000:04:00.0 \
  -- -i --nb-cores=2 --rxq=2 --txq=2
# ethtool ens4f0 → 실패 (커널에서 디바이스 사라짐)

드라이버별 DPDK PMD 종합 비교

리눅스 커널 NIC 드라이버 각각에 대응하는 DPDK PMD의 특성을 종합 비교한다. 바인딩 모델, rte_flow 하드웨어 오프로드 지원, 성숙도 등이 드라이버 선택의 핵심 기준이다.

커널 드라이버 DPDK PMD 바인딩 모델 rte_flow 최대 큐 RSS FDIR/필터 성숙도 주요 장점 주요 단점
mlx5_core net_mlx5 Bifurcated Full (최대) 1024+ 토플리츠/XOR 64M+ rules ★★★★★ bifurcated, RDMA 공존, rte_flow 완전 지원, ConnectX-7 400G 드라이버 복잡, NVIDIA FW 종속
ice net_ice VFIO 확장 (DDP) 2048 DDP 기반 16K TCAM + DDP ★★★★☆ DDP 프로파일로 유연한 파싱, AF_XDP PMD 겸용 DDP 프로파일 필수, bifurcated 미지원
i40e net_i40e VFIO 기본 지원 1536 토플리츠 FDIR 8K ★★★★★ 가장 오래 검증된 PMD, 매우 안정적, 레퍼런스 구현 기능 동결 (ice로 이전), 100G 미지원
bnxt_en net_bnxt VFIO TruFlow 512 토플리츠 EM+TCAM ★★★☆☆ TruFlow 오프로드, OVS-DPDK 지원 FW 종속성 높음, 커뮤니티 문서 부족
ixgbe net_ixgbe VFIO 기본 지원 128 토플리츠/XOR FDIR 32K ★★★★★ 10G 레퍼런스, 최고 안정성, 문서 풍부 10G 속도 한계, 기능 동결
igb net_e1000_igb VFIO 미지원 8 제한적 기본 필터 ★★★★☆ 1G 레거시 환경, 테스트/개발용 기능 매우 제한, 성능 한계
virtio_net net_virtio virtio-user 미지원 256 SW 기반 없음 ★★★★☆ vhost-user 연동, 가상화 표준, virtio-user PMD HW 오프로드 없음, 호스트 의존
vmxnet3 net_vmxnet3 VFIO 미지원 32 기본 없음 ★★★☆☆ VMware 전용 최적화 rte_flow 미지원, VMware 종속
ena net_ena VFIO 미지원 32 토플리츠 없음 ★★★☆☆ AWS 전용, LLQ 모드 저지연 rte_flow 미지원, AWS 환경 한정
cxgb4 net_cxgbe VFIO 제한적 128 기본 2K 필터 ★★☆☆☆ T5/T6 TOE/TLS 오프로드 DPDK 유지보수 소극적, 커뮤니티 지원 약함
hv_netvsc net_netvsc failsafe PMD 미지원 가변 Azure 관리 없음 ★★★☆☆ Azure failsafe — VF 핫플러그 자동 처리 간접 접근(failsafe), 성능 제한

rte_flow 오프로드 능력 상세 비교

rte_flow는 DPDK의 하드웨어 흐름 분류/오프로드 API로, 드라이버마다 지원 범위가 크게 다르다. 이 차이가 OVS-DPDK, 방화벽, 로드밸런서 등의 성능에 직결된다.

기능mlx5icei40ebnxtixgbe
Match: L2 (MAC/VLAN)
Match: L3 (IPv4/IPv6)
Match: L4 (TCP/UDP)
Match: 터널 (VXLAN/GRE/Geneve)✓ (DDP)제한적
Match: 내부 헤더 (inner L3/L4)✓ (DDP)
Action: Queue/RSS 분배
Action: Drop
Action: Mark/Flag제한적
Action: Modify (NAT)✓ (TruFlow)
Action: Encap/Decap✓ (TruFlow)
Action: Count
Action: Meter (QoS)
Action: CT (Connection Tracking)
최대 룰 수수백만16K8K~10K32K
동적 룰 추가/삭제μs 단위ms 단위ms 단위ms 단위ms 단위
OVS-DPDK 환경 드라이버 호환성: OVS-DPDK에서 rte_flow 기반 하드웨어 오프로드를 활용하려면 mlx5 또는 bnxt가 사실상 필수다. 특히 CT(Connection Tracking) 오프로드, NAT 오프로드, 터널 Encap/Decap까지 완전히 HW로 처리하는 것은 현재 mlx5만 가능하다. ice/i40e는 기본적인 flow 분류만 가능하고, ena/gve/vmxnet3는 rte_flow 자체를 지원하지 않아 OVS-DPDK 오프로드가 불가능하다.

DPDK vs 커널 성능 비교 및 선택 가이드

동일 NIC에서 커널 드라이버와 DPDK PMD의 패킷 처리 성능 차이를 비교한다. 64바이트 소형 패킷, 단일 코어 기준으로 DPDK는 커널 대비 2.5~4배의 처리율을 달성한다.

DPDK vs 커널 드라이버 패킷 처리 성능 (64B, single core, Mpps) 45 35 25 15 5 Mpps (백만 패킷/초) 15 40+ mlx5 ×2.7 12 35 ice ×2.9 10 30 i40e ×3.0 10 28 bnxt ×2.8 8 14.88 ixgbe line rate! 2 8 virtio ×4.0 커널 드라이버 DPDK PMD Line Rate

testpmd 벤치마크 성능 수치

드라이버NIC 모델속도커널 (Mpps)DPDK (Mpps)향상 배율비고
mlx5ConnectX-6 Dx100G~15~40+~2.7×bifurcated, 멀티코어 시 100+ Mpps
iceE810-C100G~12~35~2.9×DDP 최적화 시 추가 향상
i40eX710-DA440G~10~30~3.0×가장 안정적인 DPDK PMD
bnxtBCM57508100G~10~28~2.8×TruFlow 오프로드 시 추가 향상
ixgbeX520-DA210G~8~14.88~1.86×10G 물리 한계(line rate) 달성
virtiovhost-user가변~2~8~4.0×vhost-user 백엔드 기준, 최대 향상률

testpmd 기본 벤치마크 명령어:

# testpmd 기본 벤치마크 (64B 패킷, io 포워딩, 2코어)
dpdk-testpmd -l 0-3 -n 4 -a 0000:03:00.0 -- \
  --forward-mode=io \
  --nb-cores=2 \
  --rxq=2 --txq=2 \
  --rxd=2048 --txd=2048 \
  --burst=64

# testpmd 내부 명령으로 통계 확인
testpmd> start
testpmd> show port stats all

# macswap 모드 (실제 트래픽 처리 시뮬레이션)
dpdk-testpmd -l 0-3 -n 4 -a 0000:03:00.0 -a 0000:03:00.1 -- \
  --forward-mode=macswap \
  --nb-cores=2 \
  --rxq=2 --txq=2

# DPDK telemetry로 실시간 모니터링
dpdk-telemetry.py

DPDK vs AF_XDP vs 커널 XDP 선택 가이드

DPDK만이 유일한 고성능 솔루션은 아니다. AF_XDP와 커널 XDP도 상황에 따라 적합한 대안이 될 수 있다. 워크로드 특성에 따른 선택 기준을 정리한다.

기준DPDK PMDAF_XDP (유저스페이스 XDP)커널 XDP
데이터 경로유저스페이스, 커널 완전 우회유저스페이스, 커널 XDP 경유커널 내부 (드라이버 레벨)
성능 (64B)★★★★★ (40+ Mpps)★★★★☆ (20~25 Mpps)★★★★☆ (25~35 Mpps DROP)
커널 기능 유지✗ (별도 바인딩 필요, bifurcated 제외)✓ (커널 드라이버 유지)✓ (커널 드라이버 유지)
프로그래밍 모델rte_* API (독자 생태계)AF_XDP 소켓 + libxdpBPF/XDP 프로그램 (C + clang)
NIC 드라이버 지원PMD 별도 구현 필요모든 XDP 지원 드라이버모든 XDP 지원 드라이버
zero-copy기본 (mempool)드라이버 의존 (mlx5, ice, i40e)해당 없음 (커널 내부)
운영 복잡도높음 (hugepage, 바인딩, NUMA)중간 (소켓 설정)낮음 (BPF 프로그램 로드)
적합 용도NFV, HFT, 통신사 코어, OVS-DPDK커널 통합 유지 + 고성능 필요DDoS 필터링, 방화벽, 라우팅
워크로드별 선택 결정 매트릭스:
  • 극저지연 + 최대 PPS (HFT, 통신사 5G UPF): → DPDK PMD (mlx5 bifurcated 최적)
  • 커널 기능 유지 + 고성능 패킷 처리: → AF_XDP (커널 모니터링/관리 가능)
  • L3/L4 필터링, DDoS 차단: → 커널 XDP (BPF 프로그램으로 유연한 로직)
  • 범용 서버, 관리 편의 우선: → 커널 NAPI (표준 TCP/IP 스택 사용)
  • OVS 기반 가상 네트워크: → OVS-DPDK (mlx5/bnxt의 rte_flow 오프로드 활용)
  • 가상머신 간 통신: → vhost-user + virtio PMD (zero-copy 가능)

OVS-DPDK vs OVS-kernel 성능 비교

Open vSwitch(OVS)는 가상 네트워크의 표준 스위치이며, 커널 datapath와 DPDK datapath 두 가지 모드로 동작한다.

항목OVS-kernelOVS-DPDKOVS-DPDK + HW offload
데이터 경로커널 모듈 (openvswitch.ko)DPDK PMD (유저스페이스)NIC 하드웨어 (rte_flow)
64B 처리량 (single core)~2~3 Mpps~10~15 Mpps~40+ Mpps (NIC 처리)
지연 시간~50~100 μs~10~20 μs<5 μs
CPU 사용인터럽트 기반, 유휴 시 0%PMD 폴링, 항상 100% (코어 전용)오프로드된 flow는 CPU 0%
드라이버 요구커널 드라이버DPDK PMD (vfio-pci/bifurcated)mlx5/bnxt (rte_flow full)
HW offloadtc-flower (제한적)rte_flow 기반rte_flow + CT offload

구현 가이드: 최소 골격부터 확장까지

  1. 1단계: 최소 송수신 경로ndo_open/stop/start_xmit, 단일 NAPI queue, 기본 IRQ 동작
  2. 2단계: 안정성 확보 — 에러 경로 정리, queue stop/wake 일관성, teardown 순서 검증
  3. 3단계: 운영성 확보ethtool_ops, 통계, self-test, 링 파라미터 조정
  4. 4단계: 성능 확장 — 멀티큐 RSS, XDP/AF_XDP, page_pool, BQL, NUMA affinity
  5. 5단계: 가상 netdev 통합 — TUN/TAP, veth, virtio-net과 공통 코어 재사용 전략 수립
권장 학습 순서: 네트워크 스택Network Device 드라이버 (net_device)TUN/TAPBPF/XDP 순서로 보면 드라이버-스택-가속 경로가 자연스럽게 연결됩니다.