在tcp_clean_rtx_queue()中,并非对每个ACK都进行时延采样。是否进行时延采样,跟这个ACK是否为

重复的ACK、这个ACK是否确认了重传包,以及是否使用时间戳选项都有关系。

本文主要内容:tcp_clean_rtx_queue()的一些细节,时延采样的条件。

内核版本:3.2.12

Author:zhangskd @ csdn

记分牌

TCP_SKB_CB(skb)->sacked称为skb的记分牌,用于跟踪skb的状态。

以下是skb记分牌可能的取值:

#define TCPCB_SACKED_ACKED 0x01 /* skb被SACKed */
#define TCPCB_SACKED_RETRANS 0x02 /* skb retransmitted */
#define TCPCB_LOST 0x04 /* skb判断为LOST */
#define TCPCB_TAGBITS 0x07 /* All tag bits */
#define TCPCB_EVER_RETRANS 0x80 /* Ever retransmitted frame */
#define TCPCB_RETRANS (TCPCB_SACKED_RETRANS | TCPCB_EVER_RETRANS)

TCPCB_SACKED_RETRANS和TCPCB_EVER_RETRANS有什么区别呢?

当一个skb被重传了,它的记分牌就会添加TCPCB_RETRANS标志。

当重传的skb被sack确认或者ack累积确认时,就会清除TCPCB_SACKED_RETRANS标志,

但是这个时候TCPCB_EVER_RETRANS还保留,表示这个skb曾经被重传过。

TCPCB_EVER_RETRANS在更新RTT中发挥作用,如果skb曾经重传过,那么就不根据它来计算RTT

采样值,更新srtt和rto等。

释放段

从发送队列sk->sk_write_queue上移除skb,并非从物理内存中释放skb。

static inline void tcp_unlink_write_queue(struct sk_buff *skb, struct sock *sk)
{
__skb_unlink(skb, &sk->sk_write_queue);
} static inline void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list)
{
struct sk_buff *next, *prev;
list->qlen--; /* 发送队列长度减1 */
next = skb->next;
prev = skb->prev;
skb->next = skb->prev = NULL;
next->prev = prev;
prev->next = next;
} static inline void sk_wmem_free_skb(struct sock *sk, struct sk_buff *skb)
{
sock_set_flag(sk, SOCK_QUEUE_SHRUNK); /* 设置标志,表示有skb释放了*/
sk->sk_wmem_queue -= skb->truesize; /* 更新发送队列所占内存*/
sk_mem_uncharge(sk, skb->truesize);
__kfree_skb(skb); /* 释放skb占用的内存*/
}

Reno ACK处理

场景:Reno中收到ACK的时候。

这时候会模拟SACK,更新tp->sacked_out和tp->reordering。

/* Account for ACK, ACKing some data in Reno Recovery phase. */
static void tcp_remove_reno_sacks(struct sock *sk, int acked)
{
struct tcp_sock *tp = tcp_sk(sk); if (acked > 0) {
/* One ACK acked hole. The rest eat duplicate ACKs.
* 收到正常ACK,不在Recovery状态。
* 收到Cummulative ACK,确认完所有的sacked包。
*/
if (acked - 1 >= tp->sacked_out)
tp->sacked_out = 0;
else /* 收到Partial ACK,确认了部分sack包*/
tp->sacked_out -= acked - 1;
} /* 检查是否有乱序,有的话更新tp->reordering */
tcp_check_reno_reordering(sk, acked);
tcp_verify_left_out(tp);
}

时延采样

(1)什么情况下进行RTT采样?

首先,如果是重复的ACK,即没有使snd_una前进的ACK,那么是不会进入RTT更新路径的:

if (flag & FLAG_ACKED)

{

...

tcp_ack_update_rtt(sk, flag, seq_rtt);

tcp_rearm_rto(sk);

...

}

其次,有确认新数据的情况下:

1. 没有使用时间戳选项(tcp_ack_no_tstamp)

确认的数据段中不能包含重传过的段(FLAG_RETRANS_DATA_ACKED)。

因为我们无法知道是哪个包,原始包还是重传包触发了这个ACK,因此无法确定触发包的

发送时间。

如果此ACK确认了多个段,则使用第一个被确认段的发送时间来计算seq_rtt。

2. 使用时间戳选项(tcp_ack_saw_tstamp)

RTTM rule:A TSecr value received in a segment is used to update the averaged

RTT measurement only if the segment acknowledges some new data, i.e, only if

it advances the left edge of the send window.

此时我们知道这个ACK的触发段的确切发送时间为:tp->rx_opt.rcv_tsecr,所以我们计算得到

的RTT总是正确的,而不用去考虑触发这个ACK的是原始包还是重传包。

(2)icsk_ca_ops->pkts_acked()的调用

如果被此ACK确认的数据段中,含有重传过的数据段,那么就设置FLAG_RETRANS_DATA_ACKED,

ca_ops->pkts_acked(sk, pkts_acked, rtt_us)中的rtt_us = -1。

当有FLAG_ACKED标志(FLAG_DATA_ACKED | FLAG_SYN_ACKED)时调用。

有FLAG_RETRANS_DATA_ACKED设置,表示此ACK确认的是重传的数据包,这个时候rtt_us为-1。

#define FLAG_NOT_DUP (FLAG_DATA | FLAG_WIN_UPDATE | FLAG_ACKED)

Q: 那么,在什么情况下无法观测到正常的RTT呢?

A: 重传数据包的ACK、Dup ACK、Partical ACK、Cumulative ACK。

(3)各个状态下的时延采样

1. Open、CWR

对每个ACK都进行RTT采样。

2. Disorder

dup ACK,不进行RTT采样。

cumulative ACK,进行RTT采样。

3. Recovery

dup ACK,不进行RTT采样。

partical ACK

不启用Timstamp,不进行RTT采样。

启用Timestamp,进行RTT采样。

cumulative ACK

不启用Timstamp,不进行RTT采样。

启用Timestamp,进行RTT采样。

4. Loss

partical ACK

不启用Timstamp,不进行RTT采样。

启用Timestamp,进行RTT采样。

cumulative ACK

不启用Timstamp,不进行RTT采样。

启用Timestamp,进行RTT采样。

(4)重置超时重传定时器

每次更新SRTT和RTO后,都重置超时重传定时器。

/* Restart timer after forward progress on connection.
* RFC2988 recommends to restart timer to now + rto.
*/
static void tcp_rearm_rto(struct sock *sk)
{
const struct tcp_sock *tp = tcp_sk(sk); if (! tp->packets_out) { /* 如果网络上没有数据,删除超时重传定时器*/
inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS); } else { /* 重置超时重传定时器*/
inet_csk_reset_ximit_timer(sk, ICSK_TIME_RETRANS, inet_csk(sk)->icsk_rto, TCP_RTO_MAX);
}
}

TCP的核心系列 — 重传队列的更新和时延的采样(二)的更多相关文章

  1. TCP的核心系列 — 重传队列的更新和时延的采样(一)

    重传队列实际上就是发送队列(sk->sk_write_queue),保存着发送且未确认的数据段. 当有新的数据段被确认时,需要把这些段从重传队列中删除,同时更新一些变量,包括 packets_o ...

  2. TCP的核心系列 — ACK的处理(一)

    TCP发送数据包后,会收到对端的ACK.通过处理ACK,TCP可以进行拥塞控制和流控制,所以 ACK的处理是TCP的一个重要内容.tcp_ack()用于处理接收到的ACK. 本文主要内容:TCP接收A ...

  3. TCP的核心系列 — SACK和DSACK的实现(五)

    18版本对于每个SACK块,都是从重传队列头开始遍历.37版本则可以选择性的遍历重传队列的某一部分,忽略 SACK块间的间隙.或者已经cache过的部分.这主要是通过tcp_sacktag_skip( ...

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

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

  5. TCP的核心系列 — SACK和DSACK的实现(二)

    和18版本相比,37版本的SACK和DSACK的实现做了很多改进,最明显的就是需要遍历的次数少了, 减少了CPU的消耗.37版的性能提升了,代码有大幅度的改动,逻辑也更加复杂了. 本文主要内容:37版 ...

  6. TCP的核心系列 — SACK和DSACK的实现(七)

    我们发送重传包时,重传包也可能丢失,如果没有检查重传包是否丢失的机制,那么只能依靠超时来恢复了. 37版本把检查重传包是否丢失的部分独立出来,这就是tcp_mark_lost_retrans(). 在 ...

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

    上篇文章中我们主要说明如何skip到一个SACK块对应的开始段,如何walk这个SACK块包含的段,而没有涉及到 如何标志一个段的记分牌.37版本把给一个段打标志的内容独立出来,这就是tcp_sack ...

  8. TCP的核心系列 — SACK和DSACK的实现(四)

    和18版本不同,37版本把DSACK的检测部分独立出来,可读性更好. 37版本在DSACK的处理中也做了一些优化,对DSACK的两种情况分别进行处理. 本文主要内容:DSACK的检测.DSACK的处理 ...

  9. TCP的核心系列 — ACK的处理(二)

    本文主要内容:tcp_ack()中的一些细节,如发送窗口的更新.持续定时器等. 内核版本:3.2.12 Author:zhangskd @ csdn 发送窗口的更新 什么时候需要更新发送窗口呢? (1 ...

随机推荐

  1. EBS销售订单挑库发放处理程序

    来自:http://blog.csdn.net/cunxiyuan108/article/details/6014769 在EBS实施中,经常遇到从外部传进来一个被登记的销售订单,需要通过程序进行销售 ...

  2. 获取imageView的图和背景图

    img1和img2都是ImageView,要把img1中的图片显示到img2中 前景(对应src属性) img2.setImageDrawable(img1.getDrawable()); 背景(对应 ...

  3. iOS7编程Cookbook中例15.8中一个小问题

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 该书的15.8例子标题为Editing Videos on a ...

  4. T-SQL 中的CROSS JOIN用法(半翻译)

    突然发现个很吊的链接,我们来看看学习数据库要做些什么,胆小慎点:DBA工作内容!!!! 今天来翻译一篇关于T-SQL的文章,本文可供微软认证70-461:QueryingMicrosoft SQL S ...

  5. 如何使用excel画甘特图

    甘特图小伙伴们都非常的熟悉,首先小编简单的向各位小伙伴介绍一下什么是甘特图,甘特图内在思想简单,即以图示的方式通过活动列表和时间刻度形象地表示出任何特定项目的活动顺序与持续时间.基本是一条线条图,横轴 ...

  6. 【Unity技巧】四元数(Quaternion)和旋转

    四元数介绍 旋转,应该是三种坐标变换--缩放.旋转和平移,中最复杂的一种了.大家应该都听过,有一种旋转的表示方法叫四元数.按照我们的习惯,我们更加熟悉的是另外两种旋转的表示方法--矩阵旋转和欧拉旋转. ...

  7. (NO.00005)iOS实现炸弹人游戏(十一):怪物之火精灵

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 从本篇开始我们一次介绍一下游戏中敌人的制作过程.看过第一篇的小 ...

  8. 随机采样和随机模拟:吉布斯采样Gibbs Sampling实现高斯分布参数推断

    http://blog.csdn.net/pipisorry/article/details/51539739 吉布斯采样的实现问题 本文主要说明如何通过吉布斯采样来采样截断多维高斯分布的参数(已知一 ...

  9. 收藏了4年的Android 源码分享

    Android 超过2个G的源代码集合~~几乎涵盖了所有功能效果的实现,一应俱全~~应有尽有~~ 360云盘地址:Android 各类源码集合汇总 (提取码:f930) 另外,附上Github上及自己 ...

  10. 06 Activity的启动模式 Intent的七大属性的总结

    1.Task以及back stack     >Task(任务)  为了完成一个功能  多个Activity的集合,     当你的应用程序启动时 系统会自动创建Task用于管理Activity ...