服务器随机丢包但 ping 延迟正常的 perf + bpftrace 抓包定位

9次阅读

ping延迟正常但业务连接频繁超时或重传,因ping走独立ICMP路径,而TCP业务丢包可能发生在网卡驱动、软中断队列、GRO合并失败、XDP/tc策略等底层环节,需用eBPF和内核统计综合定位。

服务器随机丢包但 ping 延迟正常的 perf + bpftrace 抓包定位

为什么 ping 延迟正常但业务连接频繁超时或重传

因为 ping 只发 ICMP echo Request,走的是内核独立的 ICMP 处理路径,不经过 TCP/IP 的连接管理、队列调度、拥塞控制等环节。真实业务(如 http、gRPC)用的是 TCP,丢包可能发生在:网卡驱动收包中断丢失、软中断队列溢出、sk_buff 分配失败、socket 接收缓冲区满、甚至 eBPF 程序中误 drop。此时 ping 依然能通,但 tcpdump 在应用侧抓不到包,说明丢包发生在更底层。

perf record -e ‘net:netif_receive_skb’ 抓不到丢包点怎么办

这个 tracepoint 只在数据帧成功进入协议第一入口时触发,如果丢包发生在网卡 DMA 后但尚未调用 netif_receive_skb()(比如 NAPI poll 被延迟、RX ring 溢出、驱动丢包),它就完全沉默。必须上到更底层:

  • perf record -e 'skb:kfree_skb' 观察是否大量 skb 在极短时间内被释放,且 comm 字段为 ksoftirqd 或网卡驱动名(如 ixgbe),说明软中断处理不过来
  • 检查 /proc/net/snmpudp: 行的 noportinerrors,或 Tcp: 行的 EstabResetsAttemptFails —— 这些是内核统计的真实丢包信号
  • 确认网卡是否启用了 LRO/GRO:ethtool -k eth0 | grep gro;GRO 合并失败时可能静默丢弃分片,且不记入常规计数器

bpftrace 跟踪 sk_buff 生命周期的关键位置

不能只盯 netif_receive_skb,要串联从 DMA 到 socket 的完整链路。以下 bpftrace one-liner 可快速定位异常释放点:

bpftrace -e ' kprobe:__kfree_skb {   @kfree_reason[ksym(func)] = count(); } kretprobe:dev_gro_receive /retval == NULL/ {   @gro_fail[ustack] = count(); } tracepoint:skb:kfree_skb /args->reason == 12/ {   printf("DROPPED in %s (reason=12=SKB_DROP_REASON_PKT_TOO_SMALL)n", ksym(args->func)); }'

注意:reason == 12 对应 SKB_DROP_REASON_PKT_TOO_SMALL,常见于 GRO 合并后校验失败;dev_gro_receive 返回 NULL 表示 GRO 流水线拒绝该包,往往因 IP ID 不连续或 TCP 选项不一致 —— 这类丢包不会进 tcpdump,但会显著影响长连接吞吐。

确认是否是 XDP 层或 tc ingress 丢包

如果服务器部署了 Cilium、Calico 或自定义 tc eBPF 策略,丢包可能发生在比 netif_receive_skb 更早的位置。检查:

  • tc Filter show dev eth0 ingress —— 是否有 bpf 类型 filter 且 action 是 drop
  • ip link show eth0 查看是否有 xdp 标记;再运行 bpftool net show 确认 XDP 程序挂载状态
  • bpftrace -e 'kprobe:generic_xdp_tx { @xdp_drop = count(); }' 配合 xdp:xdp_exception tracepoint,判断是否触发了 XDP 异常路径

这类丢包完全绕过内核协议栈,netstat -s/proc/net/snmp 都不会体现,只能靠 eBPF 实时观测或驱动日志(dmesg | grep -i xdp)。

text=ZqhQzanResources