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를 활용한 병목 분석까지 실전 네트워크 드라이버 개발에 필요한 핵심을 다룹니다.
핵심 요약
- net_device — 인터페이스의 공통 상태와 콜백 진입점입니다.
- net_device_ops — open/stop/xmit 등 데이터 경로 계약을 정의합니다.
- NAPI — RX 인터럽트 폭풍을 줄이고 폴링 기반 처리량을 확보합니다.
- BQL — TX 큐 지연(latency)과 버퍼블로트 리스크를 줄입니다.
- 가상 netdev — TUN/TAP처럼 하드웨어 없이도 동일한 netdev 모델을 재사용합니다.
단계별 이해
- 수명주기 설계
할당/등록/해제 순서를 먼저 확정합니다. - RX/TX 콜백 구현
ndo_start_xmit()와 NAPI poll 루프를 정확히 연결합니다. - 운영 인터페이스 연결
ethtool_ops, 통계, 링크 상태(phylink)를 연결합니다. - 가상 netdev 확장
TUN/TAP, veth, virtio-net과 공통 패턴을 통합해 이해합니다.
개념 예시가 표시된 블록은 구조와 호출 계약 이해용이며, 실습 예제가 표시된 블록은 사용자 공간에서 실행/검증 절차를 바로 적용할 수 있도록 구성했습니다.
개요: net_device 드라이버의 역할
struct net_device 드라이버는 커널 네트워크 스택과 실제/가상 링크 계층 사이의 어댑터입니다. 유저스페이스 입장에서는 eth0, ens3, tap0 모두 동일한 netdev 인터페이스처럼 보이지만, 내부 구현은 물리 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_open | ip link set up | IRQ/NAPI 활성화, RX/TX queue 시작 |
ndo_stop | ip link set down | queue 정지, IRQ 비활성화, NAPI 비활성화 |
ndo_start_xmit | 송신 경로 | skb를 TX ring에 게시하고 doorbell 트리거 |
ndo_get_stats64 | 통계 조회 | race-safe한 64-bit 통계 제공 |
ndo_set_features | offload 변경 | 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;
}
napi_gro_receive() 경로와 page recycling 전략을 함께 설계해야 합니다.
고속 NIC에서는 RX ring refill 정책이 drop/jitter를 크게 좌우합니다.
RX 전체 파이프라인
아래 다이어그램은 물리 와이어 수신부터 애플리케이션 소켓 버퍼 도달까지의 전체 수신 경로를 보여줍니다. 각 단계에서 일어나는 핵심 동작에 주의하세요.
GRO (Generic Receive Offload) 동작 원리
GRO는 네트워크 스택 진입 전에 동일 flow의 연속 패킷들을 하나의 대형 sk_buff로 집계(merge)하여
프로토콜 스택 처리 횟수를 줄이는 소프트웨어 오프로드 기술입니다. 하드웨어 LRO(Large Receive Offload)와 달리
GRO는 프로토콜 레이어에서 stateless하게 동작하여 포워딩 환경에서도 안전하게 사용할 수 있습니다.
GRO 집계 과정은 다음과 같습니다:
- NAPI poll에서
napi_gro_receive()호출 시, GRO 엔진은napi→gro_hash[]테이블에서 동일 flow를 검색합니다. - 매칭되는 flow가 있으면 현재 패킷의 payload를 기존 skb의
frag_list또는frags[]에 병합합니다. - flow가 완료되거나(PSH, FIN 등) 타이머/카운트 제한에 도달하면 집계된 대형 skb를
netif_receive_skb()로 전달합니다. - 매칭되지 않으면 새 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 재활성화 ... */
}
ethtool -K eth0 gro off를 고려하세요.
반대로, 벌크 TCP 전송(파일 서버, 스트리밍)에서는 GRO를 반드시 활성화해야 CPU 사용률이 크게 줄어듭니다.
NAPI 상태 머신
NAPI는 명확한 상태 전환 규약을 가진 상태 머신으로 동작합니다. 드라이버가 이 규약을 정확히 따르지 않으면 인터럽트 누수(IRQ 재활성화 누락)나 poll 미진입 같은 심각한 버그가 발생합니다.
| 상태 플래그 | 비트 | 의미 |
|---|---|---|
NAPI_STATE_SCHED | 0 | NAPI가 poll 목록에 스케줄됨. napi_schedule()가 설정, napi_complete_done()이 해제 |
NAPI_STATE_DISABLE | 1 | napi_disable() 진행 중. SCHED 비트 해제를 spin-wait |
NAPI_STATE_NPSVC | 2 | busy polling 서비스 중 표시. poll이 non-softirq 컨텍스트에서 실행됨을 나타냄 |
NAPI_STATE_LISTED | 3 | NAPI가 디바이스의 napi_list에 등록됨 (netif_napi_add()가 설정) |
NAPI_STATE_NO_BUSY_POLL | 4 | busy polling 비활성화 표시 (드라이버가 미지원 선언) |
NAPI_STATE_IN_BUSY_POLL | 5 | 현재 busy poll 실행 중 (re-entrant 방지) |
NAPI_STATE_PREFER_BUSY_POLL | 6 | 소켓이 busy poll 선호 표시 (SO_PREFER_BUSY_POLL) |
NAPI_STATE_THREADED | 7 | threaded NAPI 모드 활성 — 전용 커널 스레드에서 poll 실행 |
NAPI_STATE_SCHED_THREADED | 8 | threaded 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()은 내부적으로 NAPI_STATE_SCHED 비트를 spin-wait하며, 진행 중인 poll이
napi_complete_done()을 호출해 SCHED를 해제할 때까지 대기합니다. 따라서 napi_disable() 반환 후에는
poll 콜백이 절대 실행되지 않음이 보장되며, 이후 ring 메모리를 안전하게 해제할 수 있습니다.
반드시 IRQ 비활성화 후, ring 해제 전에 호출해야 합니다.
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/메모리 자원 제한 가능
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 완료 인터럽트, 디스크립터 회수까지의 전체 송신 경로를 보여줍니다.
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의 핵심은 lib/dynamic_queue_limits.c에 구현된 DQL(Dynamic Queue Limits) 알고리즘입니다.
드라이버가 패킷을 큐에 넣을 때(dql_queued)와 완료될 때(dql_completed)를 추적하여,
현재 inflight(미완료) 바이트가 LIMIT을 초과하면 큐를 멈추고,
완료 시 실제 사용 패턴에 따라 LIMIT을 올리거나 내립니다.
- 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)을 반영하여 불필요한 여유를 제거합니다.
/* 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초) */
};
limit과 num_completed는 TX 완료 경로(보통 softirq)에서 빈번히 갱신되므로
____cacheline_aligned_in_smp로 분리하여 num_queued/adj_limit(송신 경로)와의 false sharing을 방지합니다.
드라이버 API 통합
드라이버가 BQL을 사용하려면 TX 경로의 세 지점에서 API를 호출합니다. 모든 API는 바이트 단위로 동작하며, 패킷 수가 아닌 누적 바이트를 전달해야 합니다.
/* 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 파일 | 읽기/쓰기 | 설명 |
|---|---|---|
limit | R | 현재 동적 LIMIT (바이트). 알고리즘이 자동 조정 |
limit_max | R/W | LIMIT 상한. 낮추면 최대 큐 깊이를 제한 |
limit_min | R/W | LIMIT 하한. 높이면 최소 처리량 보장 |
hold_time | R/W | slack 관찰 윈도우 (ms). 기본 1000 |
inflight | R | 현재 미완료 바이트 (num_queued − num_completed) |
BQL과 qdisc/TC의 상호작용
BQL은 qdisc 아래, NIC ring 위에 위치합니다. 패킷 흐름에서 BQL의 정확한 위치를 이해하면
fq_codel 같은 AQM(Active Queue Management)과의 시너지를 극대화할 수 있습니다.
- 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 경로까지 하나의 동시성 계약으로 맞춰야 합니다.
| 항목 | 일반 TX 경로 | LLTX 경로 |
|---|---|---|
| 직렬화 주체 | 코어/큐 락 + 드라이버 보조 락 | 드라이버가 전적으로 책임 |
| 병목 위치 | 락 경합은 비교적 예측 가능 | 드라이버 구현 품질에 따라 편차 큼 |
| 디버깅 난이도 | 표준 패턴과 도구가 많음 | race 재현/분석 난이도 높음 |
| 권장도 | 신규 구현 권장 | 기존 드라이버 유지보수 목적 외 비권장 |
LLTX를 유지해야 하는 코드베이스라면 아래 4가지를 반드시 고정 규칙으로 문서화해야 합니다.
- xmit 직렬화 규칙
ndo_start_xmit()의 re-entry 허용 범위(전역/큐별)를 명시하고 락 순서를 고정 - queue 상태 전이 규칙
netif_tx_stop_queue()/netif_tx_wake_queue()호출 조건을 단일 함수로 중앙화 - completion 메모리 순서
descriptor reclaim 이후 wake 판단 전까지의 barrier 규칙을 아키텍처별로 검증 - 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;
}
통계와 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 eth0 | TSO/GRO/checksum offload 상태 |
ethtool -S eth0 | 링 드롭, 에러, 큐별 카운터 |
ethtool -l eth0 | 채널(RX/TX queue) 구성 |
ip -s link show dev eth0 | 커널 링크 통계의 상위 뷰 |
링크 계층: PHY, phylib, phylink
현대 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;
}
}
멀티큐, 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/RFS | HW 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_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 | 규칙 우선순위/충돌 처리 |
| eSwitch | switchdev, representor netdev | VF/representor 일관성 |
| 암호화/터널 | xfrm offload, UDP tunnel offload | fallback 경로와 통계 구분 |
리셋/장애 복구: 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();
}
- watchdog:
ndo_tx_timeout()에서 즉시 heavy reset을 수행하지 말고 workqueue로 이관 - devlink health: reporter dump/recover 콜백으로 운영팀의 장애 자동화와 연동
- AER 연계: PCIe fatal/non-fatal 이벤트를 reset state machine과 통합
오프로드 계약: 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 offload | NETIF_F_HW_CSUM, skb->ip_summed | partial checksum descriptor 구성 |
| TSO/GSO | NETIF_F_TSO*, gso_size | 세그먼트 제한, header split 처리 |
| GRO/LRO | napi_gro_receive() | 재조립 후 메타데이터 일관성 |
| VLAN offload | NETIF_F_HW_VLAN_CTAG_TX/RX | tag 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;
}
- PHC:
/dev/ptpN제공과ptp4l/phc2sys연동 검증 - TX timestamp: skb 소유권과 timestamp completion 경합 방지
- RX filter: 지원 가능한 timestamp filter를 정확히 반환
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;
}
검증 매트릭스: 릴리스 전 체크 항목
| 영역 | 필수 검증 | 합격 기준 예시 |
|---|---|---|
| 기능 | up/down, MTU, VLAN, bridge, bond | 10k회 반복 시 누수/lockup 없음 |
| 성능 | 단일/다중 스트림, 작은 패킷/점보 프레임 | 목표 PPS/Gbps 달성, drop rate 임계 이내 |
| 안정성 | link flap, reset storm, hotplug, suspend/resume | 자동 복구 성공, 수동 재로드 불필요 |
| 가시성 | ethtool/stat/devlink health dump | 장애 원인 식별 가능한 텔레메트리 제공 |
| 보안 | XDP/tc rule 경계값, malformed packet | crash 없이 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;
}
- XPS:
/sys/class/net/<dev>/queues/tx-*/xps_cpus와 드라이버 큐 매핑 일치 - RFS/RPS와 충돌 회피: RX CPU와 TX completion CPU가 지나치게 분산되지 않도록 설계
- NUMA: queue memory, IRQ, NAPI poll CPU를 같은 노드로 묶어 캐시 효율 확보
Doorbell 메커니즘과 DMA 메모리 배리어
약한 메모리 모델 CPU(ARM64 등)에서 descriptor write와 doorbell MMIO write의 순서가 보장되지 않으면 간헐적 TX hang이 생깁니다. 게시 경로에서 barrier 사용 규칙을 문서화해야 합니다.
Doorbell이란?
Doorbell은 PCIe BAR 공간의 장치 레지스터에 MMIO write를 수행하여 NIC에 새 작업(디스크립터)이 준비되었음을 알리는 메커니즘입니다. Doorbell이 없으면 NIC이 링 버퍼를 지속적으로 폴링해야 하며, 이는 PCIe 대역폭 낭비와 전력 소모 증가를 초래합니다.
Doorbell의 핵심 속성은 다음과 같습니다.
- 단일 원자적 MMIO write: 일반적으로 4바이트(32비트) 크기이며, CPU의 단일 스토어 명령으로 실행됩니다.
- PCIe 트랜잭션 비용: doorbell 1회당 수백 나노초(200~500 ns)가 소요되며, PCIe TLP(Transaction Layer Packet) 오버헤드를 포함합니다.
- 메모리 배리어 선행 필수: doorbell 이전에 반드시 적절한 메모리 배리어(
dma_wmb())가 선행되어야 디바이스가 완전한 디스크립터를 읽을 수 있습니다.
서브시스템별 Doorbell 비교
Doorbell 메커니즘은 NIC뿐 아니라 다양한 PCIe 디바이스 서브시스템에서 사용됩니다. 아래 표는 주요 서브시스템별 doorbell 특성을 비교합니다.
| 서브시스템 | Doorbell 대상 | 기록 값 | 최적화 기법 |
|---|---|---|---|
| NIC TX | Tail Pointer 레지스터 | 마지막 디스크립터 인덱스 | 배치 게시 |
| NVMe | SQ Tail Doorbell | 큐 tail 포인터 | Shadow Doorbell Buffer |
| xHCI (USB 3.x) | Doorbell Array | EP 인덱스 | 스트림 기반 배치 |
| NTB (PCI) | Doorbell 비트맵 | 이벤트 비트 | 비트마스크 |
Doorbell 최적화 기법
Doorbell은 PCIe MMIO 트랜잭션이므로 호출 빈도를 줄이는 것이 성능 최적화의 핵심입니다. 주요 기법은 다음과 같습니다.
- 배치 게시(Batch Posting): 여러 디스크립터를 작성한 후 doorbell을 1회만 수행합니다. NVMe의
commit_rqs콜백이 대표적인 패턴으로, 다수의 SQ entry를 기록한 뒤 마지막에 한 번만 tail doorbell을 갱신합니다. NIC 드라이버에서도xmit_more플래그를 확인하여 배치 doorbell을 구현할 수 있습니다. - Shadow Doorbell: MMIO 대신 호스트 메모리 영역에 tail 값을 기록하고, 디바이스가 해당 메모리를 폴링하는 방식입니다. NVMe 1.3+ 스펙의 Shadow Doorbell Buffer가 대표적이며, PCIe MMIO 왕복 비용을 제거하여 수십 퍼센트의 IOPS 향상을 달성할 수 있습니다.
- Write Combining: doorbell 레지스터가 위치한 PCIe BAR 페이지를 WC(Write Combining) 매핑하여, 여러 doorbell write가 단일 PCIe 트랜잭션으로 합쳐지도록 합니다. 이를 통해 PCIe TLP 오버헤드를 줄이고 대역폭 효율을 향상시킵니다.
/* 개념 예시: 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 doorbell | dma_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
회귀 테스트: 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 |
| PFC | priority별 pause on/off 적용 | drop 급증 또는 head-of-line blocking |
| ETS | bandwidth share를 HW arbitration에 반영 | 대역폭 분배 불일치 |
PREEMPT_RT와 NAPI threaded 모드
실시간 커널에서는 IRQ/softirq 모델이 일반 커널과 다르게 동작합니다. 드라이버는 spinlock 길이를 줄이고, napi poll 지연 상한을 보장하도록 설계해야 합니다.
- 긴 IRQ-off 구간 제거: TX reclaim 루프를 짧게 쪼개고 예산 기반 처리 유지
- 락 경쟁 최소화: 큐별 락 분리, 필요 시 lockless ring 고려
- 우선순위 설계: IRQ thread/NAPI 실행 CPU를 RT 태스크와 분리
- 지연 측정:
cyclictest와 네트워크 부하를 동시 실행해 tail latency 확인
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);
}
보안 하드닝: 입력 검증과 경계 조건
네트워크 드라이버 취약점은 원격 트리거 가능성이 있습니다. 길이 검증, ring index 범위 확인, DMA 주소 검증은 성능 최적화보다 우선되어야 합니다.
| 취약 패턴 | 점검 포인트 | 방어 전략 |
|---|---|---|
| RX length 신뢰 | HW가 넘긴 length를 그대로 사용 | 최소/최대 길이, headroom 검증 |
| ring index overflow | producer/consumer wrap 처리 누락 | mask 기반 인덱싱 + assert |
| UAF on reset | reset 중 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로 전달합니다.
/* 개념 예시: 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 보호 영역: 장치 등록/이름 변경/링크 설정 같은 제어 경로
- NAPI 컨텍스트: RX poll은 softirq 문맥에서 동작
- TX 락: 멀티큐에서 queue별 락 또는 lockless ring 설계
- RCU: XDP program pointer, filter table 조회 경로
- 메모리 배리어: descriptor 게시 전 doorbell write ordering 보장
rtnl_lock이 per-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 카운터 |
| 링크 flap | PHY state machine/interrupt storm | dmesg, 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분 체크리스트
- 장치 인식 확인
lspci -nn,dmesg | grep -i <driver>로 probe 성공 여부 확인 - 링크 기본 동작
ip link set dev eth0 up,ethtool eth0로 speed/duplex/link 검증 - 기본 송수신
ping, 단일iperf3로 RX/TX 모두 정상 동작 확인 - 오프로드/큐 설정
ethtool -k/-l/-x확인 후 장비 정책에 맞게 조정 - 에러 카운터 스냅샷
ethtool -S eth0초기값 저장, 10분 부하 후 증분 비교 - 리셋 복구
의도적 link flap 또는 함수 리셋 후 자동 복구 성공 여부 확인 - 관측/알람 연계
링크/에러/reset 지표가 모니터링 시스템에 수집되는지 검증
코드 리뷰 체크리스트 (net_device 전용)
- 수명주기:
register_netdev()이후/이전 해제 순서가 정확한가 - NAPI: poll budget 준수, complete/unmask 순서가 안전한가
- TX 계약: queue stop/wake,
NETDEV_TX_BUSY반환 조건이 일관적인가 - 동기화: RTNL, spinlock, RCU 경계가 명확하고 lock inversion 위험이 없는가
- 오프로드: advertise feature와 실제 HW 동작이 일치하는가
- 에러 경로: DMA map 실패, IRQ 요청 실패, reset 실패 시 자원 정리가 누락되지 않는가
- 텔레메트리: 장애 분석에 필요한 통계/로그/devlink dump가 충분한가
제조사별 NIC 드라이버 상세 매트릭스
같은 net_device 모델이라도 벤더별로 펌웨어 의존성, 오프로드 범위, reset 전략이 다릅니다. 운영 환경에서는 “벤더별 특성”을 분리해서 튜닝/장애 대응해야 재현성이 올라갑니다.
Intel: e1000e / igb / ixgbe / i40e / ice / idpf
Intel은 리눅스 네트워크 드라이버 생태계에서 가장 오랜 역사와 가장 넓은 드라이버 포트폴리오를 보유하고 있습니다. 1GbE 데스크톱용 e1000e부터 100GbE 데이터센터용 ice, 그리고 차세대 가상화 인터페이스 idpf까지 세대별 설계 철학이 뚜렷하게 달라집니다. 각 드라이버의 아키텍처 차이를 이해하면 성능 튜닝과 장애 진단 전략을 드라이버 세대에 맞춰 정확히 수립할 수 있습니다.
| 드라이버 | 주요 세대/용도 | 핵심 포인트 | Max Queues | XDP 지원 | 자주 보는 이슈 |
|---|---|---|---|---|---|
e1000e | 1GbE 서버/임베디드 | 안정성 우선, 기능 단순 | 1 (싱글 큐) | 미지원 | 링크 flap, 절전 전환 후 wake 지연 |
igb | 1GbE 멀티큐 | RSS/TSO 기본, SR-IOV 일부 모델 | 8 | 지원 (5.9+) | 큐 불균형, IRQ affinity 미스매치 |
ixgbe | 10GbE(82599/X540) | Flow Director, DCB, XDP 일부 경로 | 128 | 지원 (4.17+) | tx timeout, FDIR rule 관리 복잡도 |
i40e | XL710/X710(40/10GbE) | VF 관리, DDP/firmware 의존성 | 64 (per PF) | 지원 (5.1+) | firmware 호환성, reset 후 VF 상태 불일치 |
ice | E810(100GbE) | devlink/representor, 고급 tc offload | 256 | 지원 (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가 지원하는 주요 칩 변형은 다음과 같습니다. 세대별로 전력 관리와 부가 기능에 차이가 있으며, 특히 I219는 Intel vPro 플랫폼의 AMT(Active Management Technology) 트래픽을 처리하는 ME(Management Engine) 연동 경로가 있어 드라이버 동작이 미묘하게 다릅니다.
| 칩 변형 | 대표 제품 | 최대 속도 | PCIe Gen | 전력 상태 | WoL | HW Timestamp | 비고 |
|---|---|---|---|---|---|---|---|
| 82574L | 독립형 PCIe GbE | 1Gbps | Gen1 x1 | D0/D3hot | 지원 | 미지원 | 가장 안정적인 레거시 칩 |
| I217-V/LM | Haswell LPC 통합 | 1Gbps | 내부 버스 | D0/D3hot/D3cold | 지원 | 미지원 | PHY 내장, Low Power Idle(EEE) |
| I219-V/LM | Skylake+ PCH 통합 | 1Gbps | 내부 버스 | D0/D3hot/D3cold | 지원 | 미지원 | ME 연동, vPro AMT 트래픽 분리 |
ethtool --set-eee eth0 eee off로 EEE를 비활성화하고, D3cold 진입 후 WoL 복구가 느린 경우 BIOS에서 Deep Sleep 옵션을 비활성화하세요.
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가 권장됩니다.
| 칩 변형 | 포트 수 | 최대 큐 | SR-IOV VF | PTP HW TS | PCIe Gen | 비고 |
|---|---|---|---|---|---|---|
| I350 | 2/4 | 8 | 최대 8 VF | 소프트웨어 TS | Gen2 x4 | 서버용 멀티포트 |
| I210 | 1 | 4 | 미지원 | 하드웨어 TS | Gen2 x1 | PTP 최적, Qbv TSN 지원 |
| I211 | 1 | 2 | 미지원 | 미지원 | Gen1 x1 | 데스크톱/NAS용 저가 |
# 실습 예제: 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) 등 데이터센터급 기능을 폭넓게 제공합니다.
| 기능 | 82599 (X520) | X540 | X550 |
|---|---|---|---|
| 최대 속도 | 10Gbps (SFP+) | 10Gbps (Base-T) | 10Gbps (Base-T/SFP+) |
| PCIe | Gen2 x8 | Gen3 x4 | Gen3 x4 |
| RSS 큐 | 128 | 128 | 128 |
| SR-IOV VF | 64 | 64 | 64 |
| Flow Director | ATR + Perfect (8K) | ATR + Perfect (8K) | ATR + Perfect (8K) |
| DCB (TC) | 8 TC | 8 TC | 8 TC |
| XDP | 지원 (4.17+) | 지원 (4.17+) | 지원 (4.17+) |
| VXLAN/GENEVE 오프로드 | 미지원 | 미지원 | 지원 |
| NBASE-T (2.5G/5G) | 미지원 | 미지원 | 지원 (X550-T) |
| MACsec | 미지원 | 미지원 | 일부 SKU |
tx_timeout이 간헐적으로 발생하는 경우, tx_ring->next_to_use와 next_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의 리소스 요청을 중재합니다.
| AdminQ 명령 카테고리 | 대표 명령 | 설명 |
|---|---|---|
| 스위치/필터 | ADD_FILTER, REMOVE_FILTER | MAC/VLAN/클라우드 필터 추가/삭제 |
| VSI 관리 | ADD_VSI, UPDATE_VSI | 가상 스테이션 인터페이스 생성/수정 |
| 큐 설정 | CONFIG_VSI_QUEUES | TX/RX 큐 매핑 및 파라미터 설정 |
| 링크 관리 | GET_LINK_STATUS, SET_PHY_CONFIG | 링크 상태 조회, PHY 설정 변경 |
| RSS | SET_RSS_KEY, SET_RSS_LUT | RSS 해시 키 및 indirection table 설정 |
| NVM/FW | NVM_UPDATE, GET_FW_VERSION | 펌웨어 업데이트, 버전 조회 |
| VF 제어 | SET_VF_CONFIG, RESET_VF | VF 리소스 할당, VF 리셋 |
| FW 버전 범위 | 지원 커널 | 주요 기능 변경 | 알려진 이슈 |
|---|---|---|---|
| 6.x | 4.x ~ 5.4 | 기본 AdminQ, VF 128개 | VF reset 후 큐 불일치 |
| 7.x | 5.4 ~ 5.15 | Cloud Filter, 향상된 VF trust | 일부 FW/드라이버 조합에서 link flap |
| 8.x ~ 9.x | 5.15+ | DDP 지원, ADQ(Application Device Queue) | ADQ + SR-IOV 동시 사용 시 성능 저하 |
| 비교 항목 | i40e (XL710) | ice (E810) |
|---|---|---|
| 최대 속도 | 40GbE | 100GbE |
| FW 제어 모델 | AdminQ | AdminQ (확장) |
| VF 프로토콜 | virtchnl v1 | virtchnl 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/인프라 드라이버 역할을 목표로 합니다.
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 (XL710/X710) | ICE (E810) | 진화 방향 |
|---|---|---|---|
| 최대 속도 | 40GbE | 100GbE | 대역폭 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 가속 |
| PTP | SW timestamp | HW timestamp (ns급) | 정밀 시간 동기화 |
| Adaptive ITR | 지원 | 지원 (4μs 단위) | 세밀한 코얼레싱 |
| XDP | 5.1+ | 5.5+ | 성능 향상 |
| RDMA | iWARP (irdma) | iWARP + RoCEv2 (irdma) | 프로토콜 범위 확장 |
linux-firmware 패키지가 최신인지 반드시 확인하세요.
ICE eswitch: Representor 모델과 Switchdev
ICE E810은 eswitch(Embedded Switch)를 통해 NIC 내부에 가상 스위치를 구성하고, 각 VF에 대응하는 representor netdev를 호스트에 노출합니다. 이 모델은 OVS(Open vSwitch) TC offload의 핵심 인프라이며, switchdev 모드로 전환하면 호스트의 TC subsystem이 NIC 하드웨어 필터 규칙을 직접 제어할 수 있습니다.
Representor 모델은 NIC 내부의 각 VF 포트에 대응하는 호스트 측 netdev(representor)를 생성합니다. OVS나 TC는 이 representor를 일반 netdev처럼 다루면서 스위칭 규칙을 설정하고, ICE 드라이버가 해당 규칙을 NIC 하드웨어에 오프로드합니다. Slow path(매칭 실패) 패킷은 representor를 통해 호스트 OVS로 올라와 소프트웨어 처리됩니다.
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
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 가상화 |
| switchdev | representor + TC 규칙 | 지원 (hw-offload) | flower/u32 | SDN, 클라우드 오버레이 |
{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"
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 mismatch | FW 업데이트: devlink dev flash pci/... file ice_pkg.bin |
| 패키지 파일 손상 | ice: DDP invalid signature | linux-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 tcp4 및 ethtool --show-ntuple eth0으로
현재 사용 규칙 수와 최대 용량을 확인할 수 있다.
i40e AdminQ Timeout 복구
i40e 드라이버에서 AdminQ(Administration Queue) timeout은 PF와 펌웨어 간 명령 채널이 응답하지 않을 때 발생한다. 이 오류는 FW hang, PCI 전송 오류, 또는 과도한 AdminQ 명령 큐잉으로 발생한다.
AdminQ timeout 감지: i40e_aq_send_msg_to_vf가
I40E_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 affinity | cat /proc/interrupts | 큐당 전용 CPU 코어 | 멀티큐 드라이버 전체 |
| Adaptive Coalescing | ethtool -c eth0 | 워크로드별 조정 | 모든 드라이버 |
| GRO/TSO | ethtool -k eth0 | 활성화 유지 | 모든 드라이버 |
| XPS (TX 큐 → CPU 매핑) | cat /sys/class/net/eth0/queues/tx-*/xps_cpus | NUMA 로컬 CPU | 멀티큐 드라이버 전체 |
| Busy Polling | sysctl net.core.busy_poll | 50 (μs), 저지연 환경 | 모든 드라이버 |
| NUMA 친화 | cat /sys/class/net/eth0/device/numa_node | NIC 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 데몬의 자동 분배 대신 수동 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_core가 HCA(Host Channel Adapter) 초기화, 커맨드 인터페이스, EQ/CQ 관리 등의 공통 기반을 제공하며, 그 위에 이더넷(mlx5e), InfiniBand/RoCE(mlx5_ib), vDPA(mlx5_vdpa) 서브시스템이 독립적으로 동작한다.
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의 핵심이다.
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 기반의 정밀한 프로그래머블 데이터 경로를 제공한다.
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 결정을 내린다.
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 # 포트 카운터 (패킷 드롭 확인)
mlx5 devlink Health Reporter
mlx5 드라이버는 devlink health 프레임워크를 통해 하드웨어 오류 감지, 진단, 자동 복구를 제공한다. 각 reporter는 특정 하드웨어/소프트웨어 구성 요소를 모니터링하며, 오류 발생 시 진단 정보(dump)를 수집하고 자동 복구를 시도한다.
| 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 리셋 없이 다음 부팅 시 적용할 수 있다.
# 현재 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의 핵심 강점을 다른 드라이버와 정량적·구조적으로 비교하여, 왜 데이터센터 표준이 되었는지를 분석한다.
강점 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가 이렇게 큰 용량을 제공하는가?
- RDMA 유산: InfiniBand에서 수백만 QP(Queue Pair)를 관리하던 하드웨어 테이블 인프라를 Ethernet steering에 재활용. 다른 NIC 벤더는 이런 대규모 테이블 엔진을 보유하고 있지 않다.
- EM + TCAM 이중 구조: Exact Match 테이블(해시 기반, 대용량)과 TCAM(ternary match, 와일드카드)을 병행하여, 정확한 플로우는 EM에서 O(1)로 검색하고 서브넷/마스크 매칭은 TCAM에서 처리한다.
- Flow Table 계층(goto chain): 여러 Flow Table을
goto chain으로 연결하여 파이프라인을 구성할 수 있다. 이는 OVS offload에서 복잡한 다중 매칭 파이프라인을 단일 HW 경로로 구현하는 핵심이다.
# 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
강점 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
- 일반 NIC: 1 descriptor = 1 패킷. 35 Mpps에서 초당 35M 회 descriptor 갱신 → PCIe/CPU 오버헤드 병목
- mlx5 MPWQE: 1 WQE = 1 대형 페이지(64KB), 여러 패킷이 stride 방식으로 배치. descriptor 갱신 빈도가 수십 배 감소, page_pool 즉시 재활용
- Inline WQE: 소형 패킷은 WQE 안에 페이로드를 직접 삽입하여 DMA 왕복 제거. XDP_TX에서 추가 속도 이점
# 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 오버헤드가 급증한다.
강점 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
강점 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 환경 확인
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
강점 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
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 |
워크로드별 최적 드라이버 선택 가이드
| 워크로드 | 최적 드라이버 | 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); }'
mlx4 레거시 드라이버 및 mlx5 마이그레이션
mlx4_core/mlx4_en은 ConnectX-2, ConnectX-3, ConnectX-3 Pro를 지원하는 레거시 드라이버이다. mlx5와 비교하여 스티어링 유연성, SR-IOV 확장성, offload 기능에서 상당한 차이가 있다. ConnectX-3 Pro는 mlx4와 mlx5 모두에서 부분적으로 지원되지만, mlx4가 권장된다.
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 drop | 0이 정상; 증가 시 링 버퍼 확대 검토 |
| PCI 오류 | dmesg | grep mlx4 | AER 오류 발생 시 슬롯 교체 고려 |
| FW Health | mlx4 장치 로그 조회 | syndrome 코드 0x0이 정상 |
| QP/CQ 고갈 | rdma stat show | free 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 × 16 | 25GbE × 2 | OVS 기본, IPsec | PCIe 3.0 × 8 |
| BF2 (BlueField-2) | Cortex-A72 × 8 | 100GbE × 2 | OVS, CT, crypto, DOCA | PCIe 4.0 × 16 |
| BF3 (BlueField-3) | Cortex-A78AE × 16 | 400GbE × 2 | OVS, AI inference, Storage | PCIe 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에서만 사용 가능하다.
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초까지 대기합니다.
링(Ring) 모델은 bnxt_en의 데이터 경로 핵심입니다. 4가지 링 타입이 서로 연동하여 패킷 송수신을 처리합니다.
| 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_CFG의 async_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 부하를 제거하고, 수백만 개의 플로우 규칙을 와이어 스피드로 처리합니다.
# 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 결정 기준:
- 와일드카드 규칙 (prefix match, mask 포함) → TCAM 사용
- 5-tuple 정확 매칭 (src_ip/dst_ip/src_port/dst_port/proto 고정) → EM 사용
- 혼합 시나리오 → 계층적 룩업: TCAM 우선, 미스 시 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'
bnxt_en SR-IOV 및 devlink 지원
bnxt_en의 SR-IOV 구현은 HWRM을 통한 완전한 VF 리소스 분할을 특징으로 합니다. PF 드라이버가 HWRM 프록시 역할을 하여 VF의 모든 하드웨어 요청을 중개합니다.
# 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으로 분할합니다.
| 비교 항목 | 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)가 될 수 있습니다. 안전한 업데이트 절차는 다음과 같습니다.
- 모든 트래픽 중지:
ip link set eth0 down(모든 NPAR 파티션) - 현재 펌웨어 백업:
bnxtnvm -dev=eth0 backup - 이중화 경로 확인: 다른 NIC으로 관리 접속 유지
- 업데이트 실행:
bnxtnvm -dev=eth0 update -f new_fw.pkg - 업데이트 중 절전 모드/재부팅 금지 (전원 차단 = 브릭킹)
# 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)와의 사이드밴드 채널을 통한 원격 관리 통합입니다.
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) 협상 시 스위치 호환성 문제로 링크가 간헐적으로 드롭됩니다. 증상은 수 분~수십 분 간격으로 링크가 재협상되며, dmesg에 tg3 eth0: Link is down → Link is up이 반복됩니다.
TSO 체크섬 오류: ethtool -S의 tx_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 부하를 최소화합니다.
| 비교 항목 | 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(프레임 프리엠션)를 하드웨어 수준에서 구현합니다.
| 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) 안에서 그룹화됩니다.
데이터 경로에서 패킷은 WRIOP(Packet I/O Engine)이 처리하고, QBMan(Queue-Based Manager) 포털을 통해 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년 이상의 칩 세대를 단일 드라이버로 지원하며, 각 변형마다 고유한 버그 회피 코드가 존재합니다.
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 미지원(단일 큐) |
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개 디스크립터를 지원하며, 멀티 큐 확장 없이 단일 링으로 동작합니다. 이는 높은 패킷 수신률 환경에서 링 오버플로우를 유발할 수 있습니다.
ethtool -S로 확인할 수 있는 두 카운터의 의미는 다릅니다:
rx_missed_errors: ASIC RX FIFO 오버플로우로 인해 하드웨어 수준에서 드롭된 패킷 수. RTL 레지스터MissedPacketCount(0x4C)를 직접 반영합니다.rx_fifo_errors: 소프트웨어 NAPI 링에서 기술적으로 손실된 프레임 수.rx_missed_errors값과 동일하게 매핑됩니다.
링 오버플로우를 실시간으로 진단하려면 bpftrace로
r8169_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)로 교체.
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);
}
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 효과 미미) |
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/)는
이 독특한 아키텍처를 반영합니다.
펌웨어 버전 의존성
| 칩셋 | 최소 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 버그 다수 수정 |
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 현재 상태 확인
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에 명령을 전송했을 때
aq_hw_fw_state_check()가 응답을 기다리는 동안 타임아웃이 발생합니다. 커널 로그에FW Mailbox is full메시지가 나타납니다. - 체크섬 불일치: FW 이미지 로드 시 CRC 검증 실패.
dmesg | grep atlantic에서FW checksum mismatch확인. - 워치독 리셋: FW 내부 MIPS 코어가 응답 불가 시 하드웨어 워치독이 자동으로 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 시 동일 링을 통해 재전송합니다.
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× 느림 |
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), 그리고 암호화 가속기입니다.
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)는
제로카피 데이터 전송을 가능하게 합니다.
오프로드 성능 영향
| 모드 | 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 서버 |
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 오프로드를 통해 소프트웨어적으로도 제어 가능합니다.
필터 설정 및 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
tc flower에서 skip_sw 플래그는 소프트웨어 경로를 건너뛰고
하드웨어만으로 필터링하며, skip_hw는 반대로 소프트웨어만 사용합니다.
/sys/kernel/debug/cxgb4/ 디버그 파일시스템을 통해
내부 레지스터, 필터 테이블, 큐 상태, 펌웨어 로그 등을 상세히 확인할 수 있습니다.
cudbg(Chelsio Unified Debug) 도구를 사용하면
어댑터 전체 상태를 덤프하여 오프라인 분석이 가능합니다.
Cloud NIC: ENA / gVNIC
| 항목 | ENA (AWS) | gVNIC (GCP) |
|---|---|---|
| 모듈명 | ena | gve |
| 소스 위치 | drivers/net/ethernet/amazon/ena/ | drivers/net/ethernet/google/gve/ |
| 큐 인터페이스 | Admin Queue + I/O SQ/CQ | GQI / DQO (세대별) |
| 최대 큐 수 | 32 (인스턴스 타입별 상이) | 16 (인스턴스 타입별 상이) |
| 최대 대역폭 | 200 Gbps (p5.48xlarge) | 200 Gbps (C3D) |
| 저지연 프로토콜 | ENA Express (SRD) | 해당 없음 |
| XDP 지원 | O (v2.6.1+) | O (GQI 모드) |
| 라이선스 | GPL v2 | GPL v2 |
ENA (Elastic Network Adapter) 아키텍처
ENA는 AWS Nitro 하이퍼바이저와 통합된 네트워크 어댑터로, EC2 인스턴스에 고성능 네트워크 I/O를 제공합니다. ENA 디바이스는 Admin Queue를 통한 제어 경로와 I/O Submission/Completion Queue를 통한 데이터 경로를 분리하여 효율적인 패킷 처리를 달성합니다.
아키텍처 개요
ENA 드라이버는 struct ena_adapter를 중심으로 동작합니다.
초기화 시 Admin Queue를 통해 디바이스와 협상하여 지원 기능, 큐 수, MTU 등을 결정합니다.
데이터 전송은 I/O SQ(Submission Queue)에 디스크립터를 기록하고 MMIO doorbell을 울려 디바이스에 통보하는 방식입니다.
완료된 패킷은 CQ(Completion Queue)를 통해 NAPI 폴링으로 처리됩니다.
LLQ (Low Latency Queue) vs Regular Queue
ENA는 두 가지 큐 동작 모드를 지원합니다. Regular 모드에서는 디스크립터와 패킷 데이터가 모두 호스트 메모리에 존재하며, 디바이스가 DMA로 읽어갑니다. LLQ(Low Latency Queue) 모드에서는 디스크립터와 패킷 헤더를 디바이스 메모리(BAR 공간)에 직접 기록하여 DMA 왕복 지연을 제거합니다.
ENA Express (SRD — Scalable Reliable Datagram)
ENA Express는 AWS가 개발한 SRD(Scalable Reliable Datagram) 프로토콜을 활용하여 단일 흐름(single flow)의 대역폭을 최대 25 Gbps까지 확장하고, p99 지연 시간을 크게 줄이는 기능입니다. 전통적인 TCP/UDP는 5-tuple 해싱으로 단일 경로에 바인딩되지만, SRD는 여러 물리 경로에 패킷을 분산하고 수신 측에서 재정렬합니다.
ethtool -S에서 SRD 관련 통계를 확인할 수 있습니다.
인스턴스 타입별 ENA 기능 매트릭스
| 인스턴스 패밀리 | 최대 대역폭 | LLQ | ENA Express | 최대 큐 수 | Enhanced 모니터링 |
|---|---|---|---|---|---|
| m5 / c5 / r5 | 25 Gbps | O | O | 8 | O |
| m6i / c6i / r6i | 50 Gbps | O | O | 16 | O |
| m7g / c7g / r7g | 30 Gbps | O | O | 16 | O |
| m7i / c7i | 50 Gbps | O | O | 32 | O |
| hpc7g | 200 Gbps | O | O | 32 | O |
| p5.48xlarge | 3200 Gbps (EFA) | O | - | 32 | O |
| t3 / t3a | 5 Gbps | O | - | 2 | O |
큐 구성 및 인터럽트 조절
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
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 비활성화 조건: 다음 상황에서는 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 -S의 ena_srd_eligible_tx_pkts와
ena_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 설정도 함께 재적용합니다.
net.ipv4.tcp_retries2 감소)은 세션이 리셋 전에 끊길 수 있습니다.
완화 방법: (1) multi-path 애플리케이션의 경우 연결 재시도 로직 구현, (2) ENA Enhanced Networking 활성화로
리셋 빈도 최소화, (3) CloudWatch NetworkPacketsOut 지표에 알람 설정.
| 장애 유형 | dmesg 패턴 | 원인 | 대응 |
|---|---|---|---|
| AdminQ 타임아웃 | ena: admin_q timed out | Nitro Card 과부하, 인프라 점검 | 자동 리셋; 반복 시 인스턴스 교체 |
| Keep-alive 실패 | ena: Keep alive watchdog timeout | 호스트 인프라 재시작 | 자동 복구; 반복 시 AWS Support 연락 |
| TX 타임아웃 | ena: TX timeout on queue X | 큐 스톨, HW 버그 | drv 버전 업그레이드 확인 |
| 불량 완료 디스크립터 | ena: Invalid req_id | DMA 오염, 드라이버 버그 | 커널/드라이버 업데이트 |
| 메모리 할당 실패 | 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 | DQO |
|---|---|---|
| 디스크립터 형식 | 통합 디스크립터 링 | 분리된 Desc/Compl 큐 |
| 메모리 모델 | QPL (Queue Page List) 공유 메모리 | per-packet DMA 매핑 |
| 완료 처리 | 같은 링에서 완료 확인 | 별도 Completion Queue |
| 버퍼 관리 | 사전 할당된 공유 페이지 풀 | 동적 DMA 버퍼 할당 |
| XDP 지원 | O | O (5.18+) |
| Header Split | X | O |
| 큐당 최대 디스크립터 | 1024 | 4096 |
| 인터럽트 조절 | 기본 | 적응형(Dim) 지원 |
| 지원 인스턴스 | N1, N2, E2, T2D | C3, C3D, H3, A3, Z3 |
| 최대 대역폭 | 100 Gbps | 200 Gbps |
인스턴스 타입별 큐 모델 매핑
| 인스턴스 시리즈 | 큐 모델 | 최대 NIC 수 | 최대 큐 수 | 최대 대역폭 |
|---|---|---|---|---|
| N1 / N2 / N2D | GQI | 8 | 16 | 32 Gbps (100G Tier1) |
| E2 | GQI | 8 | 8 | 16 Gbps |
| T2D | GQI | 8 | 8 | 32 Gbps |
| C3 / C3D | DQO | 15 | 16 | 200 Gbps (C3D) |
| H3 | DQO | 8 | 16 | 200 Gbps |
| A3 | DQO | 15 | 16 | 200 Gbps |
| Z3 | DQO | 15 | 16 | 100 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
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의 메모리 낭비가 사라지고, 불필요한 데이터 복사도 제거됩니다.
# 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의 특성을 이해하는 것이 중요합니다.
| 항목 | 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 |
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 지표와 dmesg의
hv_netvsc 메시지를 조합하여 VF failover 횟수 추적.
가상화 NIC: virtio_net / vmxnet3 / hv_netvsc
| 항목 | virtio_net | vmxnet3 | hv_netvsc |
|---|---|---|---|
| 하이퍼바이저 | KVM / QEMU | VMware ESXi | Hyper-V / Azure |
| 모듈명 | virtio_net | vmxnet3 | hv_netvsc |
| 소스 위치 | drivers/net/virtio_net.c | drivers/net/vmxnet3/ | drivers/net/hyperv/ |
| 전송 인터페이스 | virtqueue (vring) | 공유 메모리 큐 | VMBus + VF |
| 최대 큐 수 | 호스트 설정 의존 | 32 (vHW v4+) | 64 (RSS) |
| SR-IOV VF 통합 | X (별도 구성) | UPT passthrough | O (Accelerated Networking) |
| XDP 지원 | O (5.0+) | X | O (5.6+) |
| 라이선스 | GPL v2 | GPL v2 | GPL 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에 인덱스를 기록하고 인터럽트를 발생시킵니다.
Feature Negotiation
| Feature Bit | 이름 | 설명 | 성능 영향 |
|---|---|---|---|
| 0 | VIRTIO_NET_F_CSUM | 호스트가 체크섬 오프로드 지원 | TX CPU 부하 감소 |
| 1 | VIRTIO_NET_F_GUEST_CSUM | 게스트가 체크섬 오프로드 처리 | RX CPU 부하 감소 |
| 5 | VIRTIO_NET_F_MAC | 디바이스가 MAC 주소 제공 | - |
| 11 | VIRTIO_NET_F_HOST_TSO4 | 호스트가 TCP Segmentation Offload 지원 | 대역폭 증가 |
| 15 | VIRTIO_NET_F_MRG_RXBUF | Mergeable RX Buffers | 대형 패킷 효율 향상 |
| 17 | VIRTIO_NET_F_STATUS | 링크 상태 알림 | - |
| 18 | VIRTIO_NET_F_CTRL_VQ | Control Virtqueue 지원 | 동적 설정 변경 |
| 22 | VIRTIO_NET_F_MQ | 멀티큐 지원 | 멀티코어 스케일링 |
| 25 | VIRTIO_NET_F_SPEED_DUPLEX | 속도/이중 모드 설정 | - |
| 34 | VIRTIO_F_ORDER_PLATFORM | 플랫폼 메모리 순서 사용 | 배리어 오버헤드 감소 |
| 39 | VIRTIO_F_RING_PACKED | Packed 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 최적화 팁
- 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 Virtqueue | Packed 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 QMP | ioctl(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), 적응형 인터럽트 합산을 지원합니다.
vHW 버전별 기능 매트릭스
| 기능 | vHW v1 | vHW v2 | vHW v3 | vHW v4 | vHW v5 | vHW v6 | vHW v7 |
|---|---|---|---|---|---|---|---|
| ESXi 최소 버전 | 5.5 | 6.0 | 6.5 | 6.7 | 7.0 | 7.0 U2 | 8.0 |
| 최대 TX/RX 큐 | 8/8 | 8/8 | 8/8 | 32/32 | 32/32 | 32/32 | 32/32 |
| RSS | O | O | O | O | O | O | O |
| LRO | O | O | O | O | O | O | O |
| Coalescing 세분화 | 기본 | 기본 | 확장 | 확장 | 확장 | 확장 | 확장 |
| UPT (SR-IOV) | X | O | O | O | O | O | O |
| Timestamping | X | X | X | X | O | O | O |
| Uniform Passthrough v2 | X | X | X | X | X | X | O |
| Large TX desc | X | X | X | O | O | O | O |
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
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 인증 물리 NIC | SR-IOV 지원 NIC 전체 |
| 투명성 | 게스트에 완전 투명 | 게스트가 VF 디바이스 직접 인식 |
hv_netvsc 심화
hv_netvsc는 Microsoft Hyper-V 및 Azure 환경의 반가상화 네트워크 드라이버입니다. VMBus를 통한 합성(synthetic) 경로와 SR-IOV VF를 통한 가속 네트워킹(Accelerated Networking) 경로를 단일 네트워크 인터페이스 아래에서 투명하게 관리하는 것이 핵심 특징입니다. VF가 있으면 데이터 패킷은 VF를 통해 직접 전송되고, VF가 제거되면 자동으로 합성 경로로 폴백합니다.
eth0)만 보이도록 합니다.
이 투명한 본딩(bonding) 덕분에 라이브 마이그레이션 시에도 네트워크 연결이 유지됩니다.
VF Failover 매커니즘
hv_netvsc의 핵심 기능 중 하나는 VF failover입니다. Azure 호스트는 라이브 마이그레이션, 호스트 유지보수, 또는 기타 이유로 VF를 동적으로 제거하고 재할당할 수 있습니다. hv_netvsc는 이 과정을 상위 계층에 투명하게 처리합니다: VF가 제거되면 자동으로 합성 경로로 전환하고, VF가 다시 제공되면 가속 경로로 복귀합니다.
Accelerated Networking 상세
Azure의 Accelerated Networking은 SR-IOV를 기반으로 하드웨어 데이터 경로를 VM에 직접 노출합니다. 이를 통해 합성 경로 대비 지연 시간이 약 10배 개선되고(~100μs → ~25μs), CPU 사용률이 크게 감소합니다. D/E/F/G/M 시리즈의 대부분의 인스턴스 크기(2+ vCPU)에서 지원됩니다.
| 항목 | 합성 경로 (VMBus) | Accelerated Networking (VF) |
|---|---|---|
| 데이터 경로 | VM → VMBus → Host vSwitch → NIC | VM → 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 환경 고려사항
- 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
dmesg에서 "Data path switched" 메시지를 확인하세요.
합성 경로의 성능이 예상보다 낮다면 VMBus 링 버퍼 크기를 늘리고(ethtool -G),
채널 수가 vCPU 수와 일치하는지 확인하세요.
Azure 직렬 콘솔(az serial-console connect)을 통해 네트워크 단절 시에도 VM에 접근할 수 있습니다.
제조사별 공통 디버깅 루틴
- 드라이버/펌웨어 버전 고정
ethtool -i결과를 티켓/배포 메타데이터에 저장 - 벤더 통계 키 추출
ethtool -S에서 reset/drop/queue 계열 카운터를 표준화 - link/queue 이벤트 타임라인화
dmesg, devlink health, orchestrator 이벤트를 같은 타임라인으로 병합 - fallback 경로 검증
오프로드 비활성화 후 재현 여부를 확인해 HW/드라이버/스택 원인을 분리 - 벤더별 재현 스크립트 유지
링크 flap, reset storm, queue resize, offload toggle 시나리오를 자동화
드라이버 간 종합 비교
리눅스 커널에는 수십 개의 네트워크 디바이스 드라이버가 존재하며, 각각 고유한 아키텍처 결정과 최적화 전략을 채택하고 있습니다. 이 섹션에서는 주요 드라이버를 기능, 아키텍처, 큐 모델, 인터럽트 처리, 메모리 관리 등 다양한 축으로 비교 분석합니다. 드라이버 선택 시 워크로드 특성에 따른 최적 선택을 돕기 위한 종합 레퍼런스입니다.
전체 기능 매트릭스
아래 표는 리눅스 커널 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) | ✓ | ✗ | ✗ | ✗ | ✓ | ✗ | ✓ | ✓ | ✓ |
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) |
mlx5e는 200개 이상의 매치 필드(VLAN, MPLS, TCP 플래그, IP TOS 등)를 tc flower offload로 지원하는 반면,
ena는 tc flower offload 지원이 전혀 없습니다. 같은 "지원"이라도 실제 적용 범위가 크게 다를 수 있으므로,
프로덕션 배포 전에 반드시 대상 드라이버의 구체적인 offload 범위를 tc filter show dev <intf> ingress와 커널 소스에서 검증하십시오.
아키텍처 패턴 비교
네트워크 드라이버의 아키텍처는 크게 중앙 집중형(Centralized)과 분산형(Distributed)으로 분류할 수 있습니다. 중앙 집중형은 펌웨어 컨트롤러가 패킷 처리 파이프라인 전체를 관장하며, 드라이버는 펌웨어에 명령을 전달하는 역할에 그칩니다. 반면 분산형은 하드웨어 파이프라인이 고정된 기능 블록으로 구성되고, 드라이버가 각 블록을 직접 프로그래밍합니다.
아키텍처 선택은 드라이버의 복잡도, 디버깅 용이성, 기능 확장성에 직접적인 영향을 미칩니다.
| 특성 | 중앙 집중형 (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 나쁜 설계"가 아니라, 각자의 제약 조건 안에서 최적화된 선택입니다.
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 지원) */
...
};
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 드라이버 코드는 상대적으로 작고 단순하며, 복잡한 하드웨어 로직은 펌웨어에 위임됩니다.
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; /* 커맨드별 파라미터 */
};
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 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 활용 효율에 결정적 영향을 미칩니다. 단일 큐에서 시작하여 대칭 멀티큐, 분리 큐, 완료 큐 독립 모델로 진화해 왔습니다.
링 구조와 디스크립터 포맷도 드라이버마다 상이합니다.
| 드라이버 | 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 벡터 할당 전략은 드라이버 성능에 큰 영향을 미칩니다. 관리용 벡터와 데이터 큐 벡터의 분리 방식, 공유 여부가 드라이버마다 다릅니다.
인터럽트 코얼레싱(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 |
고정 | 고정 | ✗ | — | ✗ |
net/core/dim/에 통합된 DIM(Dynamically-tuned Interrupt Moderation) 프레임워크는
패킷 수와 바이트 수를 기반으로 인터럽트 간격을 자동 조정합니다. ice, mlx5e, bnxt_en 등 최신 드라이버는
모두 이 공통 프레임워크를 활용하며, ethtool -C <dev> adaptive-rx on으로 활성화합니다.
메모리 모델 비교
DMA 매핑 수명주기와 버퍼 할당 전략은 드라이버 성능의 핵심 결정 요소입니다. 패킷당 매핑/해제 방식에서 사전 매핑 풀 방식으로 진화하면서 CPU 오버헤드가 크게 감소했습니다.
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 재활용 |
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 (클라우드 관리) | 클라우드 프로바이더 의존 |
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 | 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'
드라이버별 코얼레싱 전략
인터럽트 코얼레싱은 지연 시간(latency)과 처리량(throughput) 사이의 근본적 트레이드오프입니다. 코얼레싱 간격이 길수록 인터럽트 오버헤드가 줄어 throughput이 향상되지만, 패킷 처리 지연이 증가합니다.
주요 드라이버의 코얼레싱 파라미터를 비교합니다.
| 드라이버 | 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_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, 기본값 최적화됨 |
NUMA 친화성
멀티소켓 시스템에서 NIC의 NUMA 노드와 CPU/메모리 배치가 일치하지 않으면 크로스-노드 메모리 접근으로 인한 심각한 성능 저하가 발생합니다. 올바른 NUMA 배치는 10~30%의 성능 차이를 만들어냅니다.
# 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 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
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 지원 |
- 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;
}
- fragment 순회 비용:
nr_frags가 클수록 루프 반복 증가 — 일반적으로 nr_frags ≤ 5 권장 bpf_xdp_load_bytes()는 경계 검사를 포함하므로 단순 포인터 접근보다 느림- 멀티버퍼 XDP_REDIRECT는 각 fragment의 page_pool ref를 개별 처리 — redirect 비용이 단일 버퍼 대비 nr_frags배
- 드라이버 지원 여부 확인:
ethtool -k eth0 | grep scatter
드라이버별 커널 트레이싱 레시피 통합
아래 표는 주요 드라이버별로 즉시 사용 가능한 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 |
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 큐 수 조정
mlx5e와 ice는 가장 성숙한 ZC 구현을 제공합니다.
패킷 처리 한계
네트워크 드라이버의 이론적 최대 PPS(Packets Per Second)는 링크 속도와 패킷 크기에 의해 결정되지만, 실제 달성 가능한 PPS는 드라이버 효율성, CPU 클럭, 메모리 대역폭에 의해 제한됩니다.
병목 지점을 식별하는 방법론입니다.
| 병목 유형 | 증상 | 진단 방법 | 해결 전략 |
|---|---|---|---|
| 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 전용) |
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로 직접 접근한다.
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/MSI | MSI-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
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의 데이터 경로에 접근한다.
| 항목 | 전통적 바인딩 (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 모두 가능 |
| 운영 복잡도 | 높음 — 바인딩 스크립트, 복구 절차 필요 | 낮음 — 표준 커널 운영 워크플로우 유지 |
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, 방화벽, 로드밸런서 등의 성능에 직결된다.
| 기능 | mlx5 | ice | i40e | bnxt | ixgbe |
|---|---|---|---|---|---|
| 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) | ✓ | ✗ | ✗ | ✗ | ✗ |
| 최대 룰 수 | 수백만 | 16K | 8K | ~10K | 32K |
| 동적 룰 추가/삭제 | μs 단위 | ms 단위 | ms 단위 | ms 단위 | ms 단위 |
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배의 처리율을 달성한다.
testpmd 벤치마크 성능 수치
| 드라이버 | NIC 모델 | 속도 | 커널 (Mpps) | DPDK (Mpps) | 향상 배율 | 비고 |
|---|---|---|---|---|---|---|
| mlx5 | ConnectX-6 Dx | 100G | ~15 | ~40+ | ~2.7× | bifurcated, 멀티코어 시 100+ Mpps |
| ice | E810-C | 100G | ~12 | ~35 | ~2.9× | DDP 최적화 시 추가 향상 |
| i40e | X710-DA4 | 40G | ~10 | ~30 | ~3.0× | 가장 안정적인 DPDK PMD |
| bnxt | BCM57508 | 100G | ~10 | ~28 | ~2.8× | TruFlow 오프로드 시 추가 향상 |
| ixgbe | X520-DA2 | 10G | ~8 | ~14.88 | ~1.86× | 10G 물리 한계(line rate) 달성 |
| virtio | vhost-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 PMD | AF_XDP (유저스페이스 XDP) | 커널 XDP |
|---|---|---|---|
| 데이터 경로 | 유저스페이스, 커널 완전 우회 | 유저스페이스, 커널 XDP 경유 | 커널 내부 (드라이버 레벨) |
| 성능 (64B) | ★★★★★ (40+ Mpps) | ★★★★☆ (20~25 Mpps) | ★★★★☆ (25~35 Mpps DROP) |
| 커널 기능 유지 | ✗ (별도 바인딩 필요, bifurcated 제외) | ✓ (커널 드라이버 유지) | ✓ (커널 드라이버 유지) |
| 프로그래밍 모델 | rte_* API (독자 생태계) | AF_XDP 소켓 + libxdp | BPF/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-kernel | OVS-DPDK | OVS-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 offload | tc-flower (제한적) | rte_flow 기반 | rte_flow + CT offload |
구현 가이드: 최소 골격부터 확장까지
- 1단계: 최소 송수신 경로 —
ndo_open/stop/start_xmit, 단일 NAPI queue, 기본 IRQ 동작 - 2단계: 안정성 확보 — 에러 경로 정리, queue stop/wake 일관성, teardown 순서 검증
- 3단계: 운영성 확보 —
ethtool_ops, 통계, self-test, 링 파라미터 조정 - 4단계: 성능 확장 — 멀티큐 RSS, XDP/AF_XDP, page_pool, BQL, NUMA affinity
- 5단계: 가상 netdev 통합 — TUN/TAP, veth, virtio-net과 공통 코어 재사용 전략 수립
관련 문서
- 디바이스 드라이버 — 전체 드라이버 프레임워크 문맥
- 네트워크 스택 — 커널 패킷 경로와 NAPI 배경
- TUN/TAP — 가상 인터페이스 구현 상세
- BPF/XDP — 드라이버 레벨 초고속 패킷 처리
- AF_XDP — 유저스페이스 zero-copy 경로