tcp输入数据 慢速路径处理 tcp_data_queue_ofo
tcp_data_queue_ofo
在新内核的实现中ofo队列实际上是一颗红黑树。
在tcp_data_queue_ofo中根据序号,查找到合适位置,合并或者添加到rbtree中。
同时设置dsack和sack,准备ack给发送方。
//http://abcdxyzk.github.io/blog/2015/04/01/kernel-net-data-queue/
static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb)
{
struct tcp_sock *tp = tcp_sk(sk);
struct rb_node **p, *q, *parent;
struct sk_buff *skb1;
u32 seq, end_seq;
bool fragstolen;
/*如果收到乱序包 ,可能在传输过程中出现了 拥塞
所以检查ecn 标志如果是路由器 拥塞会设置这个标志 说明路径上存在拥塞,需要
给发送方 接收方进行拥塞处理 如果没有拥塞 需要尽快通知 发送方*/
tcp_ecn_check_ce(tp, skb); if (unlikely(tcp_try_rmem_schedule(sk, skb, skb->truesize))) {//接收缓存不够
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPOFODROP);
tcp_drop(sk, skb);//接收缓存不够 丢弃
return;
} /* Disable header prediction. */
tp->pred_flags = 0;//收到乱序包,关闭快速路径
inet_csk_schedule_ack(sk);//乱序包会快速ack NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPOFOQUEUE);
seq = TCP_SKB_CB(skb)->seq;
end_seq = TCP_SKB_CB(skb)->end_seq;
SOCK_DEBUG(sk, "out of order segment: rcv_next %X seq %X - %X\n",
tp->rcv_nxt, seq, end_seq); p = &tp->out_of_order_queue.rb_node;
if (RB_EMPTY_ROOT(&tp->out_of_order_queue)) {//ofo队列中为空,简单插入新的sack
/* Initial out of order segment, build 1 SACK. */
if (tcp_is_sack(tp)) {
tp->rx_opt.num_sacks = 1;
tp->selective_acks[0].start_seq = seq;
tp->selective_acks[0].end_seq = end_seq;
}
rb_link_node(&skb->rbnode, NULL, p);
rb_insert_color(&skb->rbnode, &tp->out_of_order_queue);
tp->ooo_last_skb = skb;
goto end;
} /* In the typical case, we are adding an skb to the end of the list.
* Use of ooo_last_skb avoids the O(Log(N)) rbtree lookup.
*/
if (tcp_try_coalesce(sk, tp->ooo_last_skb, skb, &fragstolen)) {//对于普遍场景,先尝试合并skb到上一个乱序包
coalesce_done://合并完成
tcp_grow_window(sk, skb); //尝试增加窗口通告
kfree_skb_partial(skb, fragstolen);//skb已经被合并,可以释放
skb = NULL;
goto add_sack;
}
/* Can avoid an rbtree lookup if we are adding skb after ooo_last_skb */
if (!before(seq, TCP_SKB_CB(tp->ooo_last_skb)->end_seq)) {//如果序号比ooo_last_skb大,则可以直接添加,避免查找
parent = &tp->ooo_last_skb->rbnode;
p = &parent->rb_right;//添加到ooo_last_skb的右子树
goto insert;
} /* Find place to insert this segment. Handle overlaps on the way. */
parent = NULL;//需要查找这个ofo包的添加位置
while (*p) {
parent = *p;
skb1 = rb_entry(parent, struct sk_buff, rbnode);
if (before(seq, TCP_SKB_CB(skb1)->seq)) {
p = &parent->rb_left;//比当前节点小,添加到左子树
continue;
}
if (before(seq, TCP_SKB_CB(skb1)->end_seq)) {
if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) {//序号所有部分都已经在当前节点
/* All the bits are present. Drop. */
NET_INC_STATS(sock_net(sk),
LINUX_MIB_TCPOFOMERGE);
__kfree_skb(skb);
skb = NULL;
tcp_dsack_set(sk, seq, end_seq);
goto add_sack;
}
if (after(seq, TCP_SKB_CB(skb1)->seq)) {//有部分重叠
/* Partial overlap. */
tcp_dsack_set(sk, seq, TCP_SKB_CB(skb1)->end_seq);//设置重叠部分dsack
} else {//skb1->seq = seq <= skb1->end_seq < end_seq
/* skb's seq == skb1's seq and skb covers skb1.
* Replace skb1 with skb.
*///skb中包含了全部的skb1
//使用skb替换skb1
rb_replace_node(&skb1->rbnode, &skb->rbnode,
&tp->out_of_order_queue);
//设置或合并现有dsack设置 //因为skb包含了全部skb1部分,则整个skb1都被重传了
tcp_dsack_extend(sk,
TCP_SKB_CB(skb1)->seq,
TCP_SKB_CB(skb1)->end_seq);
NET_INC_STATS(sock_net(sk),
LINUX_MIB_TCPOFOMERGE);
//释放skb1
__kfree_skb(skb1);
goto merge_right;//还要继续查看skb1的右子数有没有需要合并的部分
}
} else if (tcp_try_coalesce(sk, skb1, skb, &fragstolen)) { // skb1->seq < skb1->end_seq <= seq
goto coalesce_done;//尝试合并
}
p = &parent->rb_right;//比当前加点大,查找右子树
}
insert:
/* Insert segment into RB tree. *///找到合适位置后插入ofo队列
rb_link_node(&skb->rbnode, parent, p);
rb_insert_color(&skb->rbnode, &tp->out_of_order_queue); merge_right:
/* Remove other segments covered by skb. */
while ((q = rb_next(&skb->rbnode)) != NULL) {//查看右子树中有没需要合并的节点
skb1 = rb_entry(q, struct sk_buff, rbnode); if (!after(end_seq, TCP_SKB_CB(skb1)->seq))//没有交集,不需要合并
break;
if (before(end_seq, TCP_SKB_CB(skb1)->end_seq)) {//有交集
tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq,
end_seq);//更新dsack
break;
}
//完全包含当前节点,删除该节点,并更新dsack
rb_erase(&skb1->rbnode, &tp->out_of_order_queue);
tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq,
TCP_SKB_CB(skb1)->end_seq);
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPOFOMERGE);
tcp_drop(sk, skb1);//可以删除skb1
}
/* If there is no skb after us, we are the last_skb ! */
if (!q)//没有下一个skb了,更新ooo_last_skb
tp->ooo_last_skb = skb; add_sack:
if (tcp_is_sack(tp))
tcp_sack_new_ofo_skb(sk, seq, end_seq);
end:
if (skb) {//没有被合并//跟in-order包一样,调整窗口
tcp_grow_window(sk, skb);
skb_condense(skb);
skb_set_owner_r(skb, sk);
}
}

tcp输入数据 慢速路径处理 tcp_data_queue_ofo的更多相关文章
- tcp输入数据 慢速路径处理 && oob数据 接收 && 数据接收 tcp_data_queue
大致的处理过程 TCP的接收流程:在tcp_v4_do_rcv中的相关处理(网卡收到报文触发)中,会首先通过tcp_check_urg设置tcp_sock的urg_data为TCP_URG_NOTYE ...
- TCP数据接收及快速路径和慢速路径
概述 tcp握手完成后,收到数据包后,调用路径为tcp_v4_rcv->tcp_v4_do_rcv->tcp_rcv_established在tcp_rcv_established中处理T ...
- TCP输入 之 快速路径和慢速路径
概述 快速路径:用于处理预期的,理想情况下的数据段,在这种情况下,不会对一些边缘情形进行检测,进而达到快速处理的目的: 慢速路径:用于处理那些非预期的,非理想情况下的数据段,即不满足快速路径的情况下数 ...
- serverSpeed是一个android手机端到服务器间udp/tcp对比测速软件
https://github.com/eltld/serverSpeed https://github.com/c-wind/serverSpeed https://github.com/PeterK ...
- TCP系列36—窗口管理&流控—10、linux下的异常报文系列接收
在这篇文章中我们看一下server端在接收到异常数据系列时的处理,主要目的是通过wireshark示例对这些异常数据系列的处理有一个直观的认识,感兴趣的自行阅读相关代码和协议,这里不再进行详细介绍 在 ...
- TCP主动打开 之 第二次握手-接收SYN+ACK
假设客户端执行主动打开,已经经过第一次握手,即发送SYN包到服务器,状态变为SYN_SENT,服务器收到该包后,回复SYN+ACK包,客户端收到该包,进行主动打开端的第二次握手部分:流程中涉及到的函数 ...
- TCP的核心系列 — SACK和DSACK的实现(一)
TCP的实现中,SACK和DSACK是比较重要的一部分. SACK和DSACK的处理部分由Ilpo Järvinen (ilpo.jarvinen@helsinki.fi) 维护. tcp_ack() ...
- TCP的ACK确认系列 — 快速确认
主要内容:TCP的快速确认.TCP_QUICKACK选项的实现. 内核版本:3.15.2 我的博客:http://blog.csdn.net/zhangskd 快速确认模式 (1) 进入快速确认模式 ...
- TCP的核心系列 — SACK和DSACK的实现(三)
不论是18版,还是37版,一开始都会从TCP的控制块中取出SACK选项的起始地址. SACK选项的起始地址是保存在tcp_skb_cb结构的sacked项中的,那么这是在什么时候做的呢? SACK块并 ...
随机推荐
- CSS的背景
CSS的背景 1. 背景颜色background-color div { background-color: 颜色值; } 一般情况下元素背景颜色默认是transparent(透明). 2. 背景图片 ...
- 多测师讲解自动化测试 _RF连接数据库_高级讲师肖sir
RF连接数据库:1.Connect To Database(连接数据库)2.Table Must Exist(表必须存在)3.Check If Exists In Database(查询某条件是否存在 ...
- day37 Pyhton 网络编程04
# tcp协议和udp协议的选择问题 # tcp # 大量的连续的数据 传递文件\发送邮件 # 文件的传递 # 下载电影 # udp # 短消息类 社交软件 # qq 微信 # 在线播放视频 快会丢帧 ...
- 使用notepad++的nppexec插件格式化json和压缩json内容
1.遇到问题 因为平时需要查看json内容,有时候修改后需要压缩json,虽然已经有网页可以实现,但每次打开网页也很麻烦啊.虽然notpad++也有NPPJSONViewer这个插件,但是目前只有格式 ...
- 为什么说switch比if快
C++的switch语法 在C++中,switch只接受整型常量作为分支的值: switch (expr) { case integral-constant : \\... break; case i ...
- python实现elasticsearch操作-CRUD API
python操作elasticsearch常用API 目录 目录 python操作elasticsearch常用API1.基础2.常见增删改操作创建更新删除3.查询操作查询拓展类实现es的CRUD操作 ...
- C 和 C++ 打起来了!曾今最亲密的伙伴到现今的不爽?
70年代初,贝尔实验室创建了C语言,它是开发UNIX的副产品.很快C就成为了最受欢迎的编程语言之一.但是对于Bjarne Stroustrup来说,C的表达能力还不够.于是,他在1983年的博士论文中 ...
- 好用的C语言编程软件!工具都没有,怎么用技术改变世界呢!
好用的C语言编程软件 1.VS(Visual Studio) VS(Visual Studio) VS是目前最流行的windows平台应用程序的集成开发环境,由于大部分同学使用的都是Windows ...
- spring boot:spring security+oauth2+sso+jwt实现单点登录(spring boot 2.3.3)
一,sso的用途 ? 1,如果有多个应用系统,用户只需要登录一次就可以访问所有相互信任的应用系统. 不需要每次输入用户名称和用户密码, 也不需要创建并记忆多套用户名称和用户密码. 2,系统管理员只需维 ...
- Spring Boot 整合多点套路,少走点弯路~
持续原创输出,点击上方蓝字关注我 个人原创博客+1,点击前往,查看更多 目录 前言 Spring Boot 版本 找到自动配置类 注意@Conditionalxxx注解 注意EnableConfigu ...