Files
dpdk-blog/notes/phase0/day02-kernel-rx-path.md
T
newbie 41531c19c2 init: DPDK → DPU learning blog (VitePress)
- VitePress static site for rendering all markdown documents
- Auto-discovers .md files from dpdk-roadmap-v3 and dpdk-study-kb
- Features: full-text search, dark mode, responsive design
- Pages: roadmap, notes (Phase 0-3), product docs, knowledge base
2026-08-09 15:42:47 +00:00

4.1 KiB
Executable File

Phase 0 · Day 2: 内核中断 + 网络收包路径


一、你的系统数据

IRQ 56 (ens160-rxtx-0): CPU0:1329万  CPU1:191万  CPU2:112万  CPU3:676万
IRQ 57 (ens160-rxtx-1): CPU0:11万   CPU1:33万   CPU2:36万   CPU3:38万
IRQ 58 (ens160-rxtx-2): CPU0:19万   CPU1:37万   CPU2:43万   CPU3:42万
IRQ 59 (ens160-rxtx-3): CPU0:30万   CPU1:39万   CPU2:52万   CPU3:52万
IRQ 60 (ens160-event):  全 0 (事件队列, 未使用)

NET_RX 软中断: CPU0:1339万  CPU1:436万  CPU2:381万  CPU3:791万
ksoftirqd: 每个 CPU 一个 (PID 16/24/30/36)
  • 4 个 MSI-X 向量 → 4 个 RX 队列 → 4 个 CPU 并行收包
  • IRQ 56 在 CPU0 上触发了 1329 万次(主流量),CPU1-3 只触发了少量——IRQ affinity 配置不均

二、收包 6 道关卡

关卡1: 硬件中断
  网卡 DMA 写 RX ring → MSI-X 中断 → APIC → CPU → IDT → irq_handler
  此时在硬中断上下文,所有中断被屏蔽

关卡2: NAPI 调度
  irq_handler → napi_schedule → 关网卡中断 + 触发 NET_RX_SOFTIRQ → 返回
  源码: net/core/dev.c ____napi_schedule (line 4551)

关卡3: ksoftirqd
  ksoftirqd 被唤醒 → __do_softirq → net_rx_action → 遍历 poll_list
  源码: kernel/softirq.c + net/core/dev.c

关卡4: 驱动 poll → netif_receive_skb
  从 RX descriptor ring 取包 → alloc_skb → memcpy → netif_receive_skb(skb)
  源码: net/core/dev.c netif_receive_skb (line 5899)

关卡5: 协议分发 (L2→L3)
  __netif_receive_skb_core → skb->protocol → 查 ptype_base 哈希表 → ip_rcv()
  源码: net/core/dev.c __netif_receive_skb_core (line 5434)
        net/ipv4/ip_input.c ip_rcv (line 560)
        协议注册: net/ipv4/af_inet.c dev_add_pack (line 2065)

关卡6: TCP 收包 (L3→L4→socket)
  ip_rcv → NF_HOOK → ip_local_deliver → tcp_v4_rcv
    → __inet_lookup_established → tcp_rcv_established (fast path!)
      → tcp_queue_rcv → sk_data_ready → wake_up → epoll 返回
  源码: net/ipv4/tcp_ipv4.c (line 1999)
        net/ipv4/tcp_input.c tcp_rcv_established

三、DPDK 为什么快

内核步骤 耗时 DPDK
MSI-X → IDT → handler ~3us PMD 轮询, 无中断
napi_schedule + ksoftirqd 调度 ~5us PMD 线程永不睡眠
alloc_skb (slab 锁) ~1us mbuf 从 per-lcore cache 弹出
memcpy DMA→skb ~2us DMA 直接写 mbuf, 零拷贝
ptype_base 查找 ~0.3us 应用自己解析协议
Netfilter HOOK ~5us 不需要
ehash 查 socket ~1us rte_hash_lookup
copy_to_user ~3us 零拷贝
总计 ~20us ~1us

四、实战踩坑

坑 1: 中断全绑 CPU0

  • 现象: top 看 CPU0 si=99%, 其他 CPU 空闲
  • 根因: smp_affinity 默认只开 CPU0
  • 定位: cat /proc/irq/56/smp_affinity
  • 修复: echo ff > /proc/irq/56/smp_affinity

坑 2: NAPI budget 耗尽

  • 现象: 100G 线速达不到,ethtool -S 看 rx_missed 增长
  • 根因: net_rx_action 默认一次最多 300 包,收不够就返回
  • 定位: cat /proc/net/softnet_stat, 第三列 = "times queue full"
  • 修复: 增大 net.core.netdev_budget,或换 DPDK

坑 3: ksoftirqd 被抢占

  • 现象: 收包延迟 P99 抖动严重
  • 根因: ksoftirqd 不是实时线程,被磁盘 I/O 抢占
  • 定位: perf sched latency
  • 修复: chrt -f 1 $(pidof ksoftirqd/0) 或换 DPDK PMD

坑 4: Netfilter 静默丢包

  • 现象: tcpdump 能看到包,应用收不到
  • 根因: Docker/k8s 自动注入 iptables drop 规则
  • 定位: iptables -L -v -n, 看每条规则的计数器

坑 5: TCP backlog 满

  • 现象: 客户端 connect 超时
  • 根因: accept queue=128, 第 129 个 SYN 被内核静默丢弃
  • 定位: netstat -s | grep overflow
  • 修复: echo 4096 > /proc/sys/net/core/somaxconn

五、检验标准

  • 能画中断→NAPI→ksoftirqd→poll→netif_receive_skb→ip_rcv→tcp_v4_rcv 完整调用链
  • 能解释 NAPI 为什么要关中断、用软中断
  • 能说 ptype_base 的作用
  • 能列 DPDK 比内核快 ≥5 个原因
  • 能在 /proc/interrupts 和 /proc/softirqs 找到你的网卡