写在前面

sk_buff 是 Linux 网络栈最核心、也最容易被误解的数据结构之一。它并不是“装着一个完整网络包的结构体”,而是贯穿驱动、协议栈、队列、Socket、Netfilter、tc、BPF 与硬件 offload 的控制对象和所有权载体

从 socket truesize/owner、MSG_ZEROCOPY、error queue、drop reason 讲到 Linux 5.10 与 6.1 的 skb 演进,并总结 RK3588 BSP 的验证边界。

本文以本地 Linux 5.10.209 源码为主线;涉及演进时对照 Linux 6.1.99,涉及网卡驱动时结合 Firefly RK3588 SDK Linux 5.10.198。本文是源码分析,不把尚未执行的 QEMU、tracepoint 或开发板实验写成已验证结论。

Linux sk_buff 深度解析(八):Socket 内存、零拷贝、丢包与版本演进

阅读前先建立四个问题

阅读任何 skb 代码路径,都建议反复追问:

  1. 当前 skb->data 指向哪一层协议头,linear 与 non-linear 数据分别在哪里?
  2. 哪些字段刚被修改,下一层为什么依赖这些字段?
  3. 当前谁持有 skb shell、head、frag page 以及 socket/dst/conntrack 等外部引用?
  4. 成功、失败、重试、redirect 和丢弃分支分别由谁消费或释放 skb?

本篇核心结论

  • socket 用 truesize 而不是 wire length 计量 skb 占用,并通过 destructor 归还记账。
  • MSG_ZEROCOPY 主要避免 payload copy;user page、ubuf_info、segment 和 error queue 形成额外生命周期。
  • 6.1 的重点是 hot-path cache、page_pool recycle、multi-buffer XDP 和结构化丢包原因。
  • RK3588 BSP 必须区分通用 skb 语义、stmmac 实现和真实硬件能力,静态分析不能替代实机验证。

1. Socket 内存计量与 MSG_ZEROCOPY

结论摘要

socket 使用 skb truesize 进行收发内存记账,owner helper 把 sk 和 destructor 绑定到 skb。skb_orphan() 解除 socket owner 并执行 destructor。MSG_ZEROCOPY 不等于完全不复制所有 header,而是让 TX payload 引用用户页,通过 ubuf_info 跟踪 pinned pages;skb clone/segment/completion 共同持有该引用,最终通过 error queue 通知用户可重用内存。

一、接收内存

skb_set_owner_r(skb, sk) 定义于 kernel-5.10/include/net/sock.h:2281。典型动作:

  • 设置 skb->sk
  • 设置 destructor=sock_rfree
  • skb->truesize 增加 sk_rmem_alloc

最终释放调用 sock_rfree() 归还计量并唤醒/更新 socket 状态。

为什么不用 len:一个 64 字节包可能占用 skb slab、head allocation、page、frag 和对齐开销,真实内存远大于 wire length。

二、发送内存

skb_set_owner_w() 位于 kernel-5.10/net/core/sock.c:2091,通常:

  • 绑定 sk;
  • 设置 destructor=sock_wfree
  • 增加 sk_wmem_alloc
  • 关联 socket ref/lifetime。

TCP write/retransmit queue 长期持有 skb,发送内存只有在 ACK/释放相应 skb 引用后才真正归还;网卡 completion 与 TCP ACK 的生命周期层次不同。

三、skb_orphan

skb_orphan() 位于 kernel-5.10/include/linux/skbuff.h:2811,用于清除 socket owner:

  1. 若有 destructor,调用它归还 rmem/wmem 或其它 owner 状态;
  2. skb->sk=NULL
  3. destructor=NULL

常见于转发、进入设备、某些 clone/重传或需要解除 socket lifetime 的路径。orphan 不是释放 packet data,skb 仍然存在。

错误地 orphan 太早会让 socket accounting 过早下降;太晚会让 skb 不必要地持有 socket 引用。

四、MSG_ZEROCOPY 目标

用户发送大 payload 时,普通路径从用户空间复制到 kernel skb/page。MSG_ZEROCOPY 尝试:

  • pin 用户页;
  • 把 page fragment 引用挂入 skb;
  • header 仍可在 kernel linear head 中构造;
  • DMA 直接读取用户页;
  • 完成后异步通知用户。

因此是 payload zerocopy,不保证系统调用、协议头、分段和所有元数据都零成本。

五、ubuf_info

struct ubuf_info 记录:

  • completion callback;
  • refcount;
  • zerocopy success 状态;
  • 用户页 accounting;
  • ID/range 等通知信息。

skb shared info 的 destructor_arg 指向 ubuf_infotx_flags 标记 SKBTX_DEV_ZEROCOPY/相关状态。定义见 kernel-5.10/include/linux/skbuff.h:466,相关 helper 见 kernel-5.10/include/linux/skbuff.h:1471-1519

六、clone、GSO 与 zerocopy ref

一个用户 buffer 可能经过:

  • 大 skb;
  • GSO segment 多个 skb;
  • retransmit clone;
  • 设备发送副本。

每个仍可能访问用户页的对象必须持有 ubuf/page 引用。最后一个完成前不能通知用户复用内存。

skb_zcopy_set/clear、clone/copy/segment 路径维护 ubuf_info。某个 segment fallback copy 后,可更新 overall zerocopy success,但仍需等待其它引用。

七、completion 与 error queue

TX completion 或协议确认最终调用 zerocopy callback;通知排队入口见 kernel-5.10/net/core/skbuff.c:4602。通知通常作为 extended error skb 放入 socket error queue:

1
2
3
4
5
completion
→ build notification skb
→ sock_queue_err_skb()
→ poll/recvmsg(MSG_ERRQUEUE)
→ user receives completed ID range

error queue 中“error”不一定表示发送失败,也用于 timestamp、zerocopy completion、Wi-Fi ACK 等异步状态。

八、失败与 fallback

  • pin 超过 memlock/accounting:返回错误或 fallback copy;
  • 设备不支持 SG:软件复制/linearize;
  • 加密/协议修改要求可写:部分 copy;
  • DMA map 失败:释放当前 refs,通知失败;
  • clone/segment 中途失败:已创建对象和 ubuf refs 必须回滚;
  • socket close:仍需等待/清理 outstanding completion。

用户必须在收到 completion 前保持 buffer 不修改/不复用。

九、truesize 与 zerocopy

zerocopy payload page 仍占系统资源:

  • pinned user pages;
  • skb shell/head;
  • page refs;
  • DMA mappings;
  • retransmit/queue 生命周期。

内存 accounting 不能因为“没有 memcpy”就忽略。不同路径可能同时使用 socket wmem、locked_vm、ubuf refs 和 device queue accounting。

十、所有权表

对象 owner/ref 释放/完成
skb shell users consume/kfree
kernel head dataref skb release data
user page pin/page ref ubuf final completion
ubuf_info callback refcount last skb/segment complete
socket wmem destructor/truesize sock_wfree/orphan
notification skb error queue user recvmsg/free

十一、常见误区

  1. MSG_ZEROCOPY 不代表完全没有 CPU copy 或协议头分配。
  2. 网卡 completion 后用户页不一定立即可复用,可能还有 clone/segment/retransmit 引用。
  3. error queue 不只传错误。
  4. orphan 不释放 skb 数据,只解除 owner/destructor。
  5. truesize 仍然重要,zerocopy 不是免费内存。
  6. users、dataref、page pin、ubuf ref 和 socket wmem 是不同计数。

字段与所有权统一核对表

对象或字段 主要修改者/使用者 所有权与释放要点
sk/destructor/truesize owner helper destructor 归还 rmem/wmem 和记账引用
destructor_arg/tx_flags zerocopy helper skb/shared info 持有 ubuf_info
user page pin/ref MSG_ZEROCOPY 最后一个 completion 前用户不得复用

最小调用链

1
2
3
4
5
sendmsg(MSG_ZEROCOPY)
→ pin user pages/创建 ubuf_info
→ skb/GSO segments 持有引用
→ TX completion/协议释放
→ error queue 通知可复用范围

2. 从 kfree_skb 到结构化 drop reason

从 kfree_skb 到结构化 drop reason

结论摘要

释放 skb 不等于发生丢包:正常 TX completion、用户读取和协议消费也会释放。Linux 5.10 主要通过 kfree_skb tracepoint 记录释放位置,难以表达语义原因;Linux 6.1 的 kfree_skb_reason()enum skb_drop_reason 让调用者携带结构化原因,tracepoint 同时记录 location、protocol 和 reason,从而区分 backlog 满、checksum、header、socket filter、route 等类别。

一、正常消费与丢弃

  • consume_skb():正常路径消费完成;
  • napi_consume_skb():NAPI/TX completion 正常消费并优化回收;
  • kfree_skb():传统上常用于 drop/error,但也存在历史调用不够严格的情况。

内存结果可能相同,观测语义不同。分析 drop 不能只统计所有 skb free。

二、Linux 5.10 trace_kfree_skb

kfree_skb() 位于 kernel-5.10/net/core/skbuff.c:705,在真正释放前调用:

1
trace_kfree_skb(skb, __builtin_return_address(0));

tracepoint 定义于 kernel-5.10/include/trace/events/skb.h。可看到:

  • skb 地址;
  • location/调用地址;
  • protocol 等有限字段。

主要局限:

  • location 需要符号化;
  • 同一 helper 可能因多个条件 drop;
  • wrapper 后 location 不够接近根因;
  • 没有统一 reason taxonomy;
  • 正常/异常历史调用混杂。

三、Linux 6.1 drop reason

enum skb_drop_reason 定义于 kernel-6.1/include/net/dropreason.h:78kfree_skb_reason() 位于 kernel-6.1/net/core/skbuff.c:885

调用者:

1
kfree_skb_reason(skb, SKB_DROP_REASON_...);

tracepoint 记录 reason。普通 kfree_skb() 可映射为 NOT_SPECIFIED,便于逐步迁移旧调用点。

四、reason 分类

枚举覆盖的类型随版本演进,典型类别:

  • NOT_SPECIFIED;
  • NO_SOCKET / SOCKET_FILTER;
  • PKT_TOO_SMALL / TCP_CSUM / UDP_CSUM;
  • NETFILTER_DROP;
  • IP_INHDR / IPV6_BAD_EXTHDR;
  • NO_ROUTE;
  • CPU_BACKLOG;
  • FULL_RING / QDISC_DROP;
  • XFRM_POLICY;
  • PROTO_MEM;
  • TCP_* 状态原因。

reason 应描述最接近根因的逻辑,而不是简单写“调用了 kfree”。

五、reason 传递

深层 helper 发现错误时可能:

  • 返回 enum reason 给上层;
  • 通过输出参数设置 reason;
  • 上层统一释放并 trace;
  • skb list/segment 释放对每个成员传播同一 reason。

好处是把“判断原因”和“真正释放地点”分开后仍保留根因。

六、CPU backlog 示例

RPS enqueue 时目标 CPU 队列达到上限,5.10 通常只能从 enqueue_to_backlog() 的 kfree location 推断。6.1 可标记 SKB_DROP_REASON_CPU_BACKLOG,更直接区分:

  • 驱动 ring drop;
  • qdisc drop;
  • socket rcvbuf drop;
  • softnet backlog drop。

七、协议与 reason

tracepoint 还可记录 skb->protocol,但 protocol 字段在不同层可能:

  • 尚未设置;
  • 为 outer protocol;
  • VLAN/隧道后变化;
  • 非 Ethernet skb。

reason 与 location 仍需结合调用链、设备和协议字段解释,不能单独作为完整诊断。

八、释放链与 trace 时机

reason trace 在 users 归零并即将释放时触发。若 skb 仍有 users,当前调用只减引用,不一定产生最终 free trace。clone 共享 data 的最后一个 shell/dataref 释放时机也不同。

因此 trace 数量不等于收到包数或 drop packet 数,需要理解 clone/list/segment。

九、设计价值

  • 统一 drop taxonomy;
  • eBPF/perf/drop monitor 可按 reason 聚合;
  • 减少依赖调用地址和版本行号;
  • 支持协议逐步细化;
  • 让生产环境统计更可读;
  • 区分资源压力、策略、安全校验和协议错误。

十、常见误区

  1. 所有 kfree_skb 都算丢包是错误的。
  2. reason 不是 errno,二者服务不同层次。
  3. NOT_SPECIFIED 不代表正常,只表示调用点尚未细化。
  4. clone 的 free event 不能简单按一个 event=一个 wire packet。
  5. reason 仍需结合 location/dev/protocol/调用链。
  6. 5.10 没有 reason 枚举时仍可用 location 和协议统计,但准确性较弱。

字段与所有权统一核对表

对象或字段 主要修改者/使用者 所有权与释放要点
skb pointer free/consume/drop helper 调用后原持有者不得再访问
drop reason kfree_skb_reason() 结构化原因随 tracepoint 暴露
location/protocol 旧式 tracepoint 只能辅助定位,不能替代原因语义

3. Linux 5.10 到 6.1 的关键演进

结论摘要

5.10 到 6.1 的 skb 演进重点不是四指针模型改变,而是高性能内存回收、多 buffer XDP、GRO 分桶和丢包可观测性加强。6.1 用 NAPI skb cache bulk 操作降低 shell slab 开销,用 pp_recycle 把 page_pool 回收信息带入 skb free,用 multi-buffer XDP 对接 shared info,用结构化 drop reason 替代仅靠 location 的诊断。

一、基础模型保持稳定

两版仍保持:

  • skb shell 与 head 分离;
  • head/data/tail/end
  • shared info 位于 end;
  • len/data_len/truesize
  • frags/frag_list;
  • users/dataref/page ref;
  • clone/copy/COW/free 分层。

因此 5.10 是理解核心机制的可靠基线。

二、NAPI skb shell cache

6.1:

  • napi_skb_cache_get()kernel-6.1/net/core/skbuff.c:252
  • napi_skb_cache_put()kernel-6.1/net/core/skbuff.c:1054

per-CPU cache 配合 kmem_cache_alloc_bulk/free_bulk

  • 一次从 slab 获取/归还多个 skb shell;
  • NAPI hot path 从本地数组 pop/push;
  • 降低 allocator lock、freelist 和 cacheline 成本;
  • 适合高 PPS 小包。

5.10 已有 NAPI 分配/延迟回收思想,但 6.1 的 bulk cache 路径更系统。

三、pp_recycle 与 page_pool

6.1 struct sk_buff 增加/使用:

1
pp_recycle:1

位置:kernel-6.1/include/linux/skbuff.h:926

释放 head/frag 时:

1
2
3
4
pp_recycle set
→ page_pool_return_skb_page(page)
├─ page 属于合适 page_pool:直接回池
└─ 不能回池:普通 put_page/free

关键位置:

  • head recycle:kernel-6.1/net/core/skbuff.c:758
  • frag unref:kernel-6.1/net/core/skbuff.c:785
  • helper:kernel-6.1/include/linux/skbuff.h:5104
  • page_pool API:kernel-6.1/include/net/page_pool.h:242

5.10 驱动更常显式区分 copy page 直接 recycle 与 frag page 通用释放,跨 clone/协议栈自动回池能力较弱、驱动耦合更高。

四、clone 与 recycle 风险

clone 会复制 pp_recycle,但共享 page/dataref 带来竞态:不能让某个 clone 过早把 page 回池。6.1 在 clone/copy/expand 路径检查 recycle 状态与共享条件,例如 kernel-6.1/net/core/skbuff.c:793-802kernel-6.1/net/core/skbuff.c:5459-5463

这说明 recycle flag 不是“free 时无条件放回 pool”,必须与 page ownership/refcount 协同。

五、XDP multi-buffer

6.1:

  • xdp_buff_has_frags()include/net/xdp.h:88
  • xdp_update_skb_shared_info()include/net/xdp.h:223

XDP packet 可由主 buffer + frags 组成。PASS 转 skb 时把 fragment descriptors 写入 shared info,更新 len/data_len/truesize。5.10 native XDP 设计主要围绕单 buffer,jumbo/multi-descriptor 支持受限。

六、GRO 重构与 hash bucket

5.10 GRO 主要在 net/core/dev.c,一个 NAPI 上的候选 list 比较成本随活跃 flow 增长。

6.1:

  • 实现拆到 net/core/gro.c
  • GRO_HASH_BUCKETS=8include/linux/netdevice.h:339
  • gro_hash[]include/linux/netdevice.h:362
  • dev_gro_receive()net/core/gro.c:483

先按 skb hash 分桶,再进行协议级 same-flow 比较,减少多 flow 场景线性扫描。

七、drop reason

5.10 trace_kfree_skb 主要记录 location。6.1:

  • enum skb_drop_reason
  • kfree_skb_reason()
  • tracepoint reason;
  • 协议函数逐步返回具体 reason。

对 backlog、checksum、header、socket、policy 等根因可结构化统计。

八、skb extensions

6.1 extension ID/类型继续增加,例如 SKB_EXT_MCTP

  • enum:kernel-6.1/include/linux/skbuff.h:4626
  • size table:kernel-6.1/net/core/skbuff.c:4512

extension 让低频/可选 metadata 不必永久膨胀核心 struct sk_buff,代价是独立分配/引用和 clone/free 管理。

九、API/代码位置变化

阅读跨版本代码时需要注意:

  • GRO 函数从 dev.c 移到 gro.c;
  • API 可能增加 reason/recycle 参数;
  • inline helper 行号和字段布局变化;
  • 驱动 backport 可能只有部分新机制;
  • 同名函数实现细节不一定相同。

不能只用文件名 diff 判断功能有无,应追踪 symbol、config 和调用点。

十、对驱动开发的影响

主题 5.10 常见责任 6.1 改进
skb shell 普通/NAPI alloc/free bulk per-CPU cache
RX page 驱动显式 recycle 分支 skb free 可 pp_recycle
jumbo XDP 单 buffer 限制明显 multi-buffer metadata
GRO flow list 线性候选较多 hash buckets
drop location 推断 structured reason
optional metadata extensions 较少 extension 类型扩展

十一、常见误区

  1. 6.1 并没有推翻 skb 基础模型。
  2. pp_recycle 不等于跳过 page ref 安全。
  3. multi-buffer XDP 需要驱动和 BPF/redirect 全链支持,不只是 helper 存在。
  4. GRO 移文件不只是代码整理,还伴随数据结构/分桶演进。
  5. drop reason 的覆盖是渐进式,仍会有 NOT_SPECIFIED。
  6. BSP 5.10 可能 backport 部分 6.x 特性,必须检查本地源码而非仅看版本号。

字段与所有权统一核对表

对象或字段 主要修改者/使用者 所有权与释放要点
pp_recycle page_pool skb 路径 free 时可归还 page_pool
NAPI skb cache alloc/free hot path per-CPU cache 批量管理 skb shell
drop reason/XDP frags 6.1 新路径 增强可观测性和 multi-buffer 表达

最小调用链

1
2
3
5.10 基线 skb alloc/build/free
→ 6.1 NAPI cache/page_pool recycle/XDP frags/drop reason 路径
→ 对具体 backport/BSP 做符号级核对

4. RK3588 BSP 中的 skb 生命周期

RK3588 BSP 中的 skb 生命周期

结论摘要

RK3588 BSP 保持 Linux 5.10 skb 核心模型,平台特性主要体现在 stmmac RX/TX、page_pool、DMA descriptor、checksum/hash/offload 和 vendor patch。源码结论必须分为三层:通用 skb 语义、stmmac 驱动实现、RK3588 平台 glue/硬件能力。QEMU virt 不能验证真实 GMAC DMA/page_pool/offload,最终硬件结论需开发板串口、ethtool、抓包和性能实验。

一、版本与源码边界

  • 通用主线学习树:Linux 5.10.209,commit 6c60e1d
  • 演进树:Linux 6.1.99,commit 5ab5f07
  • Firefly BSP:Linux 5.10.198,commit 11a3d9327b6f

BSP 版本更早,且可能包含 Android KABI、Rockchip/Firefly 和 stmmac backport。判断差异需对具体文件/symbol diff,不能按 5.10.198 数字推断全部能力。

二、通用 skb 机制

BSP 仍使用:

  • struct sk_buff shell;
  • 独立 head + tail shared info;
  • linear/frags/frag_list;
  • users/dataref/page ref;
  • alloc/build/clone/copy/free;
  • NAPI/GRO/RPS/qdisc/socket ownership。

因此阶段 0~4、6~21 的通用概念仍适用,行号和 vendor 字段需以 BSP 树重新检索。

三、RK3588 GMAC 软件层次

1
2
3
4
5
6
7
8
Rockchip platform glue (dwmac-rk.c)
→ clocks/resets/GRF/PHY mode/delay
stmmac core (stmmac_main.c)
→ netdev/NAPI/RX/TX/ring/page_pool/skb
DMA descriptor ops
→ OWN/status/address/length
GMAC hardware
→ DMA/MTL/MAC/PHY

dwmac-rk.c 不直接定义 skb 核心语义,它配置平台硬件;真正构造/消费 skb 的主路径在 stmmac core。

四、RX 生命周期

1
2
3
4
5
6
7
8
9
10
11
page_pool/RX buffer
→ DMA descriptor OWN to hardware
→ frame received, OWN to CPU
→ stmmac_rx()
├─ first data copy to napi_alloc_skb linear head
├─ later segments skb_add_rx_frag
├─ checksum/hash/rx_queue/protocol
└─ napi_gro_receive
→ GRO/RX core/IP/socket
→ skb free
→ head/frags page release/recycle

关键入口:firefly_rk3588_SDK/kernel/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c:3862

BSP 实现的核心特点:

  • 首段 copy 优化 header locality;
  • 后续 page frag 降低 payload copy;
  • copy page 可直接 recycle;
  • frag page 生命周期交给 skb;
  • refill 在 descriptor 重新 OWN 前准备新 page。

五、TX 生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
socket/IP/qdisc
→ stmmac_xmit() :3468
→ map linear/frags
→ fill descriptors
→ OWN to DMA
→ BQL sent
→ hardware transmit
→ stmmac_tx_clean() :2087
→ check OWN/status
→ unmap
→ consume skb at packet completion
→ BQL completed
→ wake queue

一个 skb 占多个 descriptor 时,软件通常只在最后 descriptor 关联 skb,确保所有 mapping 完成后再 free。

六、page_pool 适用性

page_pool 为 RX 提供:

  • page cache/recycle;
  • DMA mapping 生命周期优化;
  • NAPI-local fast path;
  • 与 XDP/frag 的 memory model。

BSP 5.10 的回收更多由驱动显式管理。Linux 6.1 pp_recycle 可把回收意图带入通用 skb free。若向 BSP backport,不能只加一个 flag,还需:

  • page 标识/pool ownership;
  • clone/copy/expand 安全;
  • frag unref helper 参数;
  • head recycle;
  • 驱动 build_skb/page_pool API 对齐。

七、checksum/hash/offload

硬件能力通过 netdev features 和 descriptor status 进入 skb:

  • RX checksum → ip_summed
  • RX hash → hash/l4_hash
  • RX queue → queue_mapping
  • TX checksum → PARTIAL descriptor flags;
  • TSO/GSO → MSS/header/descriptor;
  • VLAN → tag insertion/stripping metadata。

必须同时检查:设备树/平台配置、stmmac capability、DMA feature register、netdev feature negotiation 和实际 descriptor path。

八、RPS/RFS/XPS 与单/多队列

stmmac 驱动可为软件流调度提供 hash 和 RX queue 信息。实际收益取决于:

  • GMAC queue 数;
  • RSS 硬件能力和配置;
  • IRQ affinity;
  • RPS/XPS maps;
  • CPU/cluster/NUMA(RK3588 big.LITTLE);
  • 应用绑核;
  • GRO 和包长。

源码能确定机制位置,不能仅凭源码宣称某个 mask 在实机性能更好。

九、XDP 能力边界

判断 native XDP 必须在 BSP stmmac 中找到完整闭环:

  • XDP program attach;
  • RXQ info/memory model;
  • RX poll 在 skb 前执行 XDP;
  • PASS 转 skb;
  • TX ring;
  • redirect/flush;
  • page_pool return。

若缺失,只能依赖 generic XDP。Linux 6.1 multi-buffer helper 不会自动出现在 BSP 5.10,也不能只复制 helper 而忽略驱动/API 链。

十、BSP 差异分析方法

对每个主题执行:

1
2
3
4
5
6
1. rg 找 BSP symbol 和调用点
2. 与 kernel-5.10 同文件/函数 diff
3. git log/blame 查看 vendor/backport 来源
4. 检查 CONFIG、Kconfig、Makefile
5. 检查 DTS 与 platform glue 是否启用硬件能力
6. 区分编译存在、运行启用、硬件实际支持

不要只比较 include/linux/skbuff.h 就判断完整网络能力;驱动、page_pool、net/core/dev、offload 和平台配置必须一起看。

十一、QEMU 与实机边界

QEMU virt 可以验证 arm64 Linux 协议栈、socket、qdisc、Netfilter 等通用机制,但不能真实模拟:

  • RK3588 GMAC/MTL/DMA descriptor;
  • Rockchip GRF/clock/delay;
  • 真实 PHY;
  • stmmac hardware checksum/RSS/TSO;
  • page_pool 与真实 DMA 性能。

硬件验证最终需要:串口、dmesg、ethtool -k/-S、debugfs、trace、perf、抓包、iperf 和必要的 IRQ/RPS/XPS 配置实验。本轮笔记仅完成源码分析。

十二、通用结论与 BSP 结论

结论 类型
len/data_len、clone/free 引用规则 通用 skb 语义
stmmac 首段 copy + 后续 frag 当前驱动实现
RK3588 时钟/GRF/PHY delay 平台 glue
某 offload 在板上实际有效 需实机验证
6.1 pp_recycle/drop reason 上游版本演进
BSP 是否 backport 某能力 本地源码事实

十三、常见误区

  1. RK3588 BSP 顶层不是单一 Git 仓库,kernel 子仓提交需单独记录。
  2. stmmac 通用代码存在不等于板级能力已启用。
  3. QEMU virt 启动 BSP kernel 不等于模拟真实 GMAC。
  4. 5.10 BSP 可能 backport 新机制,也可能缺少上游 5.10.209 修复。
  5. page_pool 存在不等于所有 frag 都能自动 recycle。
  6. 源码支持 XDP 配置不等于 native RX path 完整。

字段与所有权统一核对表

对象或字段 主要修改者/使用者 所有权与释放要点
DMA descriptor/page RK stmmac RX/TX 驱动、page_pool 与 DMA completion 转移所有权
skb offload metadata stmmac/BSP 硬件能力决定 checksum/hash/GSO 处理
通用 skb refs 内核 core vendor glue 不改变 users/dataref/page ref 基本规则

最小调用链

1
2
3
4
5
RK3588 GMAC descriptor/page_pool
→ stmmac RX 构造 skb
→ 通用协议栈/socket 或转发
→ qdisc/stmmac TX
→ DMA completion 与 page/skb 回收

源码阅读路线

建议不要从 struct sk_buff 声明开始逐字段背诵,而应使用下面的阅读顺序:

  1. 先在入口函数确认 skb 是新分配、clone、队列取出还是从驱动 buffer 构造;
  2. 沿成功主线记录 data/len/data_len、header offset、checksum、hash、queue mapping 等字段变化;
  3. 再回到每个失败分支,确认 skb、page、DMA、socket 和记账引用是否回滚;
  4. 最后对照 5.10、6.1 或 BSP 的同名 symbol,区分语义变化、性能优化和 vendor backport。

本篇总结

  1. socket 用 truesize 而不是 wire length 计量 skb 占用,并通过 destructor 归还记账。
  2. MSG_ZEROCOPY 主要避免 payload copy;user page、ubuf_info、segment 和 error queue 形成额外生命周期。
  3. 6.1 的重点是 hot-path cache、page_pool recycle、multi-buffer XDP 和结构化丢包原因。
  4. RK3588 BSP 必须区分通用 skb 语义、stmmac 实现和真实硬件能力,静态分析不能替代实机验证。

sk_buff 的难点从来不只是字段多,而是同一个对象不断改变数据视图、元数据状态和持有者。只要把“布局、字段、所有权、释放”四条线同时画出来,绝大多数网络栈源码都会从零散函数变成可推导的生命周期。

系列目录

  1. Linux sk_buff 深度解析(一):数据结构、四指针与核心字段
  2. Linux sk_buff 深度解析(二):非线性数据、引用计数与生命周期
  3. Linux sk_buff 深度解析(三):从网卡 DMA 到 Socket 的 RX 路径
  4. Linux sk_buff 深度解析(四):从 sendmsg 到 TX completion
  5. Linux sk_buff 深度解析(五):GRO、GSO、Checksum 与多核流调度
  6. Linux sk_buff 深度解析(六):Netfilter、tc、eBPF 与 XDP
  7. Linux sk_buff 深度解析(七):VLAN、隧道、分片与重组
  8. Linux sk_buff 深度解析(八):Socket 内存、零拷贝、丢包与版本演进(本文)