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的更多相关文章

  1. tcp输入数据 慢速路径处理 && oob数据 接收 && 数据接收 tcp_data_queue

    大致的处理过程 TCP的接收流程:在tcp_v4_do_rcv中的相关处理(网卡收到报文触发)中,会首先通过tcp_check_urg设置tcp_sock的urg_data为TCP_URG_NOTYE ...

  2. TCP数据接收及快速路径和慢速路径

    概述 tcp握手完成后,收到数据包后,调用路径为tcp_v4_rcv->tcp_v4_do_rcv->tcp_rcv_established在tcp_rcv_established中处理T ...

  3. TCP输入 之 快速路径和慢速路径

    概述 快速路径:用于处理预期的,理想情况下的数据段,在这种情况下,不会对一些边缘情形进行检测,进而达到快速处理的目的: 慢速路径:用于处理那些非预期的,非理想情况下的数据段,即不满足快速路径的情况下数 ...

  4. serverSpeed是一个android手机端到服务器间udp/tcp对比测速软件

    https://github.com/eltld/serverSpeed https://github.com/c-wind/serverSpeed https://github.com/PeterK ...

  5. TCP系列36—窗口管理&流控—10、linux下的异常报文系列接收

    在这篇文章中我们看一下server端在接收到异常数据系列时的处理,主要目的是通过wireshark示例对这些异常数据系列的处理有一个直观的认识,感兴趣的自行阅读相关代码和协议,这里不再进行详细介绍 在 ...

  6. TCP主动打开 之 第二次握手-接收SYN+ACK

    假设客户端执行主动打开,已经经过第一次握手,即发送SYN包到服务器,状态变为SYN_SENT,服务器收到该包后,回复SYN+ACK包,客户端收到该包,进行主动打开端的第二次握手部分:流程中涉及到的函数 ...

  7. TCP的核心系列 — SACK和DSACK的实现(一)

    TCP的实现中,SACK和DSACK是比较重要的一部分. SACK和DSACK的处理部分由Ilpo Järvinen (ilpo.jarvinen@helsinki.fi) 维护. tcp_ack() ...

  8. TCP的ACK确认系列 — 快速确认

    主要内容:TCP的快速确认.TCP_QUICKACK选项的实现. 内核版本:3.15.2 我的博客:http://blog.csdn.net/zhangskd 快速确认模式 (1) 进入快速确认模式 ...

  9. TCP的核心系列 — SACK和DSACK的实现(三)

    不论是18版,还是37版,一开始都会从TCP的控制块中取出SACK选项的起始地址. SACK选项的起始地址是保存在tcp_skb_cb结构的sacked项中的,那么这是在什么时候做的呢? SACK块并 ...

随机推荐

  1. Unix中使用MeteoInfo - Xmanager设置

    通过Xshell等客户端登陆Unix系统运行图形软件(X11)需要有X-server,Xmanager是其中的佼佼者(可惜是商业软件).我通常用Xshell登陆气象局的IBM高性能计算机(AIX系统) ...

  2. idea创建servlet工程初体验

    servlet工程创建 前提:创建项目之前需要配置java环境变量 和tomcat配置,配置完成后进入如下操作. tomcat 安装和配置参考 https://www.cnblogs.com/xush ...

  3. 学习ing

    1.从硬件和逻辑两个角度探讨什么是内存?硬件上看,内存就是电脑上的硬件--内存条.内存通过内存条不同的实现原谅分为DRAM(DRAM已经发展出好多代)和SRAM.从逻辑的角度来说,内存就是一个可以随机 ...

  4. 2020年sublime text3可用的注册码

    2020年sublime text3可用的注册码 最近终于找到可以用的sublime text3注册码了,仅供各位同学交流学习使用.用完请删除,购买正版! ----- BEGIN LICENSE -- ...

  5. spring boot:用spring security加强spring boot admin的安全(spring boot admin 2.3.0 / spring boot 2.3.3)

    一,spring boot admin的安全环节: 1,修改context-path,默认时首页就是admin, 我们修改这个地址可以更安全 2,配置ip地址白名单,有ip限制才安全, 我们使用了sp ...

  6. spring boot:用redis+redisson实现分布式锁(redisson3.11.1/spring boot 2.2)

    一,为什么要使用分布式锁? 如果在并发时锁定代码的执行,java中用synchronized锁保证了线程的原子性和可见性 但java锁只在单机上有效,如果是多台服务器上的并发访问,则需要使用分布式锁, ...

  7. date命令之移动修改日志日期

    [16:19:50 root@C8[ 2020-06-16DIR]#touch app.log [16:21:25 root@C8[ 2020-06-16DIR]#ll total 0 -rw-r-- ...

  8. C++学习笔记---数据类型

    1.整型 C++中能够表示整型的类型有几下几种方式,区别在于所占内存空间不足 数据类型 占用空间 取值范围 short(短整型) 2字节 (-2^15~2^15-1) int(整型) 4字节 (-2^ ...

  9. 【C++】 C++异常捕捉和处理

    在阅读别人开发的项目中,也许你会经常看到了多处使用异常的代码,也许你也很少遇见使用异常处理的代码.那在什么时候该使用异常,又在什么时候不该使用异常呢?在学习完异常基本概念和语法之后,后面会有讲解. ( ...

  10. Java中四舍五入保留两位小数

    Java中四舍五入保留两位小数 方法一 四舍五入 double f = 3.15; long res = Math.round(f); #结果 res = 3 保留两位小数 double f = 3. ...