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块并 ...
随机推荐
- 多测师讲解selenium_输入性弹框定位_高级讲师肖sir
#输入性弹框from selenium import webdriverfrom time import sleepdrvier=webdriver.Chrome()url='file:///F:\d ...
- 多测师讲解html _段落标签002_高级讲师肖sir
<html> <head> <meta charset="UTF-8"> <title>段落标签</title> < ...
- 多测师讲解python练习题_100以内奇数,偶数的和_高级讲师肖sir
(1)通过while 循环来求出1-100之和'''(2)通过while 循环来求出1-100奇数之和'''(3)通过while 循环来求出1-100偶数之和''' 奇数和 sum1=0for i i ...
- MeteoInfo脚本示例:GrADS to netCDF
这里给出一个将GrADS数据文件转为netCDF数据文件的脚本示例程序,其它格式数据转netCDF可以参考: #-------------------------------------------- ...
- 4~20mA信号采集
4-20mA信号采集 4-20mA信号采集可选卓岚ZLAN6802(485)/ZLAN6842(以太网)/ZLAN6844(无线wifi)他们不仅可以可采集4~20mA还可以采集 /0~5V/0~10 ...
- pytest文档50-命令行参数--durations统计用例运行时间
前言 写完一个项目的自动化用例之后,发现有些用例运行较慢,影响整体的用例运行速度,于是领导说找出运行慢的那几个用例优化下. --durations 参数可以统计出每个用例运行的时间,对用例的时间做个排 ...
- Spring Boot入门系列(二十一)如何优雅的设计 Restful API 接口版本号,实现 API 版本控制!
前面介绍了Spring Boot 如何快速实现Restful api 接口,并以人员信息为例,设计了一套操作人员信息的接口.不清楚的可以看之前的文章:https://www.cnblogs.com/z ...
- linux 手动配置ip地址方法
手工配置静态的IP地址 也就是手工配置IP地址.子网掩码.网关和DNS. 设置方法如下: vi /etc/sysconfig/network-scripts/ifcfg-eth0 编辑本地网卡的配置文 ...
- Java8新特性--Base64转换
1.简介 在Java8中,Base64编码已经成为Java类库的标准.Java 8 内置了 Base64 编码的编码器和解码器. Base64工具类提供了一套静态方法获取下面三种BASE64编解码器: ...
- C#一行代码实现(01)最小化到通知区域
主要功能 实现Winform程序最小化后,隐藏任务栏显示,在通知区域显示.左键点击通知区域图标,可以弹出菜单,包含开机启动和退出程序,可以根据需求进行定制. 一行代码 private void For ...