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块并 ...
随机推荐
- 【C++入门学习笔记】函数和对象!你需要这一篇文章入门C++!
一.本篇要学习的内容和知识结构概览 二.知识点逐条分析 1. 混合型语言 C++源文件的文件扩展名为.cpp, 也就是c plus plus的简写, 在该文件里有且只能有一个名为main的主函数, ...
- php生成器 yield 转
一.yield介绍 文档介绍说道:生成器函数的核心是yield关键字.它最简单的调用形式看起来像一个return申明,不同之处在于普通return会返回值并终止函数的执行,而yield会返回一个值给 ...
- CentOS 6编译安装RabbitMQ
编译安装Python 下载python源文件 [root@localhost src]# tar -xzvf python-2.7.11.tar.gz [root@localhost src]# cd ...
- ASP.NET实现进度条效果【转】
原文地址:http://www.jb51.net/article/115310.htm 这篇文章主要为大家详细介绍了ASP.NET实现简单的进度条效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一 ...
- 基于risc-v架构cpu
一.定义: CPU ,全称为中央处理器单元,简称为处理器,是一个不算年轻的概念 早在 20 世纪60 年代便己诞生了第一款 CPU请注意区分"处理器"和"处理器核& ...
- poj1011 Sticks (搜索经典好题)
poj1011 Sticks 题目连接: poj1011 Description George took sticks of the same length and cut them randomly ...
- 学习Python 能找到工作?1300+条招聘信息告诉你答案
对于python这块有任何不懂的问题可以随时来问我,我对于学习方法,系统学习规划,还有学习效率这些知道一些,希望可以帮助大家少走弯路.当然也会送给大家一份系统性的python资料,文末附有爬虫项目实战 ...
- JS 计算日期相减得天数
言简意赅不呼哨直接懂,可以封装的可以根据自己的需求封装一下 var date1="2020-10-23";var date2="2020-10-26";var ...
- ng中的ng-content ng-template ng-container
在angular中,有这样三个自带的标签,但是在angular的文档中没有说明,只有在api中有简单的描述,摸索了半天才搞懂是咋回事. ng-content <div> <ng-co ...
- python的deque(双向)队列详解
首先 python的队列有很多种 Python标准库中包含了四种队列,分别是queue.Queue / asyncio.Queue / multiprocessing.Queue / collecti ...