当前位置: 首页 > news >正文

Linux tcp_congestion_ops 拥塞控制算法结构体注册机制

Linux tcp_congestion_ops 拥塞控制算法结构体注册机制

tcp_congestion_ops 是 TCP 拥塞控制算法的接口抽象,定义在 include/net/tcp.h 中。所有拥塞控制算法(Cubic、BBR、Reno、Westwood、DCTCP 等)通过该结构体向内核注册。结构体包含拥塞控制状态的初始化、cwnd 在每个 ACK 到达时的更新、丢包事件响应、cwnd_undo(撤销错误减窗)以及 pkts_acked 速率采样等回调函数指针。

```c
struct tcp_congestion_ops {
struct list_head list;
unsigned long flags;
__u32 key;
char name[TCP_CA_NAME_MAX];
struct module *owner;

int (*init)(struct sock *sk);
void (*release)(struct sock *sk);
void (*ssthresh)(struct sock *sk);
u32 (*cwnd)(const struct sock *sk);
void (*cong_control)(struct sock *sk, const struct rate_sample *rs);
void (*cong_avoid)(struct sock *sk, u32 ack, u32 acked);
u32 (*undo_cwnd)(struct sock *sk);
void (*pkts_acked)(struct sock *sk, const struct ack_sample *sample);
void (*set_state)(struct sock *sk, u8 new_state);
...
};
```

算法注册通过 tcp_register_congestion_control 完成,函数将 ops 插入全局链表 tcp_cong_list,由读写锁 tcp_cong_list_lock 保护。模块初始化时调用 tcp_register_congestion_control(&tcp_cubic_ops),清理时调用 tcp_unregister_congestion_control 从链表中移除。注册过程对 ops->key 做了唯一性检查,防止同一算法重复注册。

```c
int tcp_register_congestion_control(struct tcp_congestion_ops *ca)
{
int ret = 0;

if (!ca->cong_control && (!ca->cong_avoid || !ca->ssthresh))
return -EINVAL;

write_lock(&tcp_cong_list_lock);
if (tcp_ca_find_key(ca->key)) {
ret = -EEXIST;
} else if (!tcp_ca_find(ca->name)) {
list_add_tail_rcu(&ca->list, &tcp_cong_list);
pr_debug("%s registered\n", ca->name);
} else {
ret = -EEXIST;
}
write_unlock(&tcp_cong_list_lock);

return ret;
}
```

关键验证:若未实现 cong_control(BBR 风格),则必须同时实现 cong_avoid 和 ssthresh(classic AIMD 风格),否则返回 -EINVAL。注册时检查 name 的唯一性,但 key 冲突则返回 -EEXIST。list_add_tail_rcu 将新算法追加到链表尾部,使旧算法(如 CUBIC 作为默认)优先级更高——tcp_ca_find 首次匹配就返回,链表头部是内置算法。

运行时,每个 TCP socket 的 icsk_ca_ops 指针通过 tcp_assign_congestion_control 赋值。该函数在 tcp_init_transfer 中调用,决定算法选择优先级:控制面通过 sysctl_tcp_congestion_control 设定,随后检查 TCP_CONGESTION sockopt(setsockopt IPPROTO_TCP TCP_CONGESTION),最后 fallback 到系统默认值。

```c
void tcp_assign_congestion_control(struct sock *sk)
{
struct net *net = sock_net(sk);
struct tcp_congestion_ops *ca;
const char *name = net->ipv4.tcp_congestion_control;

if (sk->sk_txhash)
name = tcp_ca_get_name_by_key(sk->sk_txhash & TCP_CA_KEY_MASK);
if (!name)
name = tcp_ca_get_default(sk);

rcu_read_lock();
ca = tcp_ca_find(name);
if (unlikely(!ca)) {
ca = tcp_ca_find("cubic");
}
if (ca != rcu_dereference(icsk->icsk_ca_ops)) {
rcu_assign_pointer(icsk->icsk_ca_ops, ca);
if (ca->init)
ca->init(sk);
}
rcu_read_unlock();
}
```

这里存在动态切换路径:当 __tcp_transmit_skb 处于 BH 上下文中时,icsk_ca_ops 被 rcu_assign_pointer 切换,读者须使用 rcu_dereference 读取。若在 tcp_ack 中不加 RCU 保护直接读取 icsk_ca_ops->cong_control,在并发 tcp_set_congestion_control 被 setsockopt 调用时会出现读取到空指针或中间状态。内核通过 rcu_read_lock/rcu_dereference 包裹 tcp_cong_control 中所有对 icsk_ca_ops 的访问。

拥塞控制算法切换的竞争条件:tcp_set_congestion_control 被用户态线程调用时持有 lock_sock,而 tcp_ack 运行在 BH 上下文中仅持有 bh_lock_sock。若 tcp_set_congestion_control 调用 ca->release(旧算法析构)且 ca->init(新算法构造),在 release 后 init 前的窗口内如果有 BH 到达并调用 ca->pkts_acked,会读取到已释放的 ca 指针。解决方法是调用 synchronize_net() 等待正在执行的 BH 完成,但 synchronize_net 可能睡眠,因此 tcp_set_congestion_control 必须在进程上下文中调用且不能持有 spinlock。

```c
int tcp_set_congestion_control(struct sock *sk, const char *name, bool load, bool rcu_locked)
{
struct tcp_congestion_ops *ca;
int err;

ca = tcp_ca_find(name);
if (!ca) {
if (!load)
return -ENOENT;
err = request_module("tcp_%s", name);
if (err < 0)
return -ENOENT;
ca = tcp_ca_find(name);
if (!ca)
return -ENOENT;
}

if (!try_module_get(ca->owner))
return -EAGAIN;

tcp_ca_switch(sk, ca);
module_put(ca->owner);
return 0;
}
```

icsk_ca_priv 是拥塞控制算法私有状态的存储区,尺寸固定为 96 字节。CUBIC 使用的结构体 struct bictcp(44 字节)、BBR 使用的 struct bbr(64 字节)均在此区域内,使用前通过 inet_csk_ca(sk) 强制类型转换。当私有状态超出 96 字节时,内核在 tcp_register_congestion_control 中检查 BUILD_BUG_ON 并拒绝注册。私有数据的初始化在 ca->init(sk) 回调中完成,释放时 ca->release(sk) 清理 DCTCP 的 dctcp_shadow 等额外分配内存。

pacing rate 与 cwnd 的协调:tcp_cong_control 调用 ca->cong_control 时传递 struct rate_sample,其中包含 delivered(当前 RTT 内确认的数据量)和 interval_us(确认间隔)。BBR 依赖该数据进行带宽估计,而 CUBIC 则主要使用 ca->cong_avoid 更新 snd_cwnd。两套接口的切换在 tcp_ca_dst 选择的 tcp_cong_control 内部完成:如果 ops->cong_control 不为 NULL,则不调用传统的 cong_avoid/ssthresh 路径。

http://www.jsqmd.com/news/1232150/

相关文章:

  • 整合广告素材、投放、下载、LTV、收入的移动广告情报平台对比 - 芈只AI研究院
  • Claude Code安全风险解析与全平台卸载指南
  • 2026 年至今,榆林有实力的小型污水处理设备优质厂家推荐几家,别再等了!这个小设备如何彻底解决你的污水难题? - 行业推荐官[官方】--
  • Windows刷机指南:系统重装与优化全流程
  • 2026 年现阶段盐津评价高的倒角机器定制厂家哪个好,用它,让你的零件精度瞬间提升十倍 - 行业鉴选官
  • RISC-V架构爆发与物联网芯片创新实践
  • 工控开发技术全景与2026年选型趋势
  • 从零实现C++机器学习库:深入理解张量、计算图与自动微分
  • C语言图书管理系统项目实战:数据结构、动态内存与文件操作详解
  • 《黄帝内经》011章|法于阴阳 和于术数
  • 深度解析Unrpyc:Ren‘Py脚本反编译的效率革命
  • 2026年7月山东出口六偏磷酸钠/潍坊磷酸三钠品牌实力推荐_潍坊华建裕丰新材料有限公司 - 行业平台推荐
  • 3分钟掌握专业足球数据分析:Understat异步Python包完全指南
  • Qwen3.6 27B模型在RTX显卡上的性能实测与优化
  • 2026年7月海南AI搜索优化代理商/海南GEO代理商全套系统_智链未来(海南)科技有限公司 - 品牌宣传支持者
  • Linux进程管理:从基础命令到高级实践
  • SpringBoot+Vue3博客管理系统:半小时搭建与项目深度解析
  • 汽车电子控制单元设计:车门与车窗控制器电路解析
  • 电机驱动系统设计:电源供电与驱动电路核心技术解析
  • LLM价值导向评估:从话量到质量的AI应用思维转变
  • 2026火锅店采购竹笋怎么做试菜决策:把口感、规格和收货条件放在同一张表
  • 职场高效学习系统:破除学习幻觉的实战方法论
  • 本地RAG应用实战:LangChain+Ollama+FAISS黄金组合
  • AIGC降重工具对比:千笔与Checkjie全学科适配实测
  • 2026年7月海口AI搜索推广/海口GEO代理商怎么选_智链未来(海南)科技有限公司 - 行业平台推荐
  • AI大模型在移动应用安全漏洞检测中的表现与成本效益分析
  • 2026 年新发布:缙云有实力的滑动钢制闸门实力厂家哪家可靠,别再花冤枉钱!揭秘高效闸门的关键技术 - 企业推荐官【认证】
  • AI与自动化本质区别:决策机制、学习能力与技术范式辨析
  • C++性能优化实战:缓存局部性与分支预测原理详解
  • 从零手搓C++机器学习库:深入理解自动微分与计算图实现