PRR算法(Proportional Rate Reduction)决定在丢包恢复(Loss Recovery)期间,对应于每个ACK报文,可发送的报文数量。目的是:1)快速平稳的从Loss中恢复;2)恢复之后拥塞窗口收敛与ssthresh。主要是为了解决Linux内核之前采用的恢复算法Rate-halving存在的一些弊端:

  • 在恢复阶段,为防止burst发送,内核将拥塞窗口设置为pipe+1,然而,如果由于应用程序没有数据可发送,最早将导致拥塞窗口降低为1,即使仅丢失了一个报文。
  • 在恢复之后,将拥塞窗口降低太多(一半甚至更低),但是当前内核的默认Cubic算法将ssthresh降至之前拥塞窗口的70%,太低的拥塞窗口将造成性能损失。
  • ACK报文的丢失,将导致更少报文的发送。
  • 在丢失多个报文时,容易触发RTO超时。

PRR由两个部分组成。一是当网络中报文数量(pipe)大于ssthresh时,通常是丢失较少报文时的恢复开始阶段的情况,根据ACK报文的到达成比例的降低拥塞窗口。例如对于Cubic算法,此部分通过每接收到10个ACK确认报文(10报文被对端所接收),发送7个报文的方式,将拥塞窗口降低30%(等于Cubic设置的ssthresh值)。

第二个部分是,如果网络中报文数量(pipe)小于ssthresh,通常是丢失多个报文或者应用程序在恢复阶段没有数据可发送的情况,阻止拥塞窗口的降低。RFC6937中定义了两种Reduction Bound算法:Conservative Reduction Bound (CRB)和Slow Start Reduction Bound (SSRB),前者严格遵守报文守恒机制;而后者类似SlowStart,比CRB更具侵略性,对于每个接收到的ACK报文,SSRB允许额外多发送一个数据报文。

PRR初始化

在进入TCP_CA_Recovery或者TCP_CA_CWR拥塞状态时,调用函数tcp_init_cwnd_reduction初始化PRR相关参数。prr_delivered记录在进入Recovery/CWR状态后接收端收到的报文数量,而变量prr_out用于统计进入Recovery之后,发送的报文数量。

snd_ssthresh为拥塞算法(默认Cubic,参见函数bictcp_recalc_ssthresh)计算的ssthresh值,最终,拥塞窗口将收敛与此值。

static void tcp_init_cwnd_reduction(struct sock *sk)
{struct tcp_sock *tp = tcp_sk(sk);...tp->prior_cwnd = tp->snd_cwnd;tp->prr_delivered = 0;tp->prr_out = 0;tp->snd_ssthresh = inet_csk(sk)->icsk_ca_ops->ssthresh(sk);tcp_ecn_queue_cwr(tp);

PRR更新cwnd

如下函数tcp_cwnd_reduction,如果pipe大于snd_ssthresh,即delta小于零,执行PRR算法的第一个成比例部分,如下为RFC6937给出的算法:

    RecoverFS = snd.nxt-snd.una // FlightSize at the start of recoveryOn every ACK during recovery compute:DeliveredData = change_in(snd.una) + change_in(SACKd)prr_delivered += DeliveredDatapipe = (RFC 6675 pipe algorithm)if (pipe > ssthresh) {// Proportional Rate Reductionsndcnt = CEIL(prr_delivered * ssthresh / RecoverFS) - prr_out}

内核中使用进入Recovery时的拥塞窗口prior_cwnd表示算法中的RecoverFS的值,变量dividend首先增加了prior_cwnd-1的值,在除去prior_cwnd,达到了算法中CEIL的效果。此阶段需要等比例的减小拥塞窗口,比例为:snd_ssthresh/prior_cwnd。

void tcp_cwnd_reduction(struct sock *sk, int newly_acked_sacked, int flag)
{struct tcp_sock *tp = tcp_sk(sk);int sndcnt = 0;int delta = tp->snd_ssthresh - tcp_packets_in_flight(tp);if (newly_acked_sacked <= 0 || WARN_ON_ONCE(!tp->prior_cwnd))return;tp->prr_delivered += newly_acked_sacked;if (delta < 0) {u64 dividend = (u64)tp->snd_ssthresh * tp->prr_delivered + tp->prior_cwnd - 1;sndcnt = div_u64(dividend, tp->prior_cwnd) - tp->prr_out;} else if ((flag & (FLAG_RETRANS_DATA_ACKED | FLAG_LOST_RETRANS)) ==FLAG_RETRANS_DATA_ACKED) {sndcnt = min_t(int, delta,max_t(int, tp->prr_delivered - tp->prr_out,newly_acked_sacked) + 1);} else {sndcnt = min(delta, newly_acked_sacked);}/* Force a fast retransmit upon entering fast recovery *//* 在进入快速恢复阶段时,强制发送至少一个报文(此时prr_out为零)。 */sndcnt = max(sndcnt, (tp->prr_out ? 0 : 1));tp->snd_cwnd = tcp_packets_in_flight(tp) + sndcnt;

如果当前ACK报文确认了之前重传的数据,并且没有进一步标记新的重传数据的丢失,使用PRR算法的第二部分计算发送数量。

RFC6937中定义的PRR算法的第二部分如下(else部分,linux使用PRR-SSRB部分)。如果由于应用程序缺少报文发送将导致prr_delivered的值大于prr_out的值,内核取其与新确认报文数量newly_acked_sacked值,两者之间的最大值,另外再加上1(MSS),最后,取以上结果和delta之间的较小值。此阶段增加拥塞窗口,趋近于ssthresh。

相比于PRR-CRB,SSRB的加1操作,并非严格遵守报文守恒原则,

    if (pipe > ssthresh) {...} else {// Two versions of the Reduction Boundif (conservative) { // PRR-CRBlimit = prr_delivered - prr_out} else { // PRR-SSRBlimit = MAX(prr_delivered - prr_out, DeliveredData) + MSS}// Attempt to catch up, as permitted by limitsndcnt = MIN(ssthresh - pipe, limit)}

最后,如果当前ACK报文没有确认重传数据,确认的为正常数据,或者确认了新的重传数据的丢失,发送数量设定类似于PRR-CRB,不同点在于内核使用delta与newly_acked_sacked之间的最小值。而PRR-CRB使用的是delta与prr_delivered - tp->prr_out的差值之间的最小值。此种情况下,报文发送数量不像PRR-SSRB激进,原因是一方面丢失报文较少;或者另一方面,丢失报文较多,网络拥塞严重。

    sndcnt = min(delta, newly_acked_sacked);

以上函数tcp_cwnd_reduction在tcp_ack处理完成ACK报文之后,在函数tcp_cong_control中调用。注意BBR拥塞算法(实现了自身的cong_control控制)不使用PRR。

static void tcp_cong_control(struct sock *sk, u32 ack, u32 acked_sacked,int flag, const struct rate_sample *rs)
{const struct inet_connection_sock *icsk = inet_csk(sk);if (icsk->icsk_ca_ops->cong_control) {icsk->icsk_ca_ops->cong_control(sk, rs);return;}if (tcp_in_cwnd_reduction(sk)) {/* Reduce cwnd if state mandates */tcp_cwnd_reduction(sk, acked_sacked, flag);

更新prr_out

变量prr_out记录CWR/Recovery拥塞状态发送的报文数量,内核使用函数tcp_in_cwnd_reduction判断套接口是否处于CWR/Recovery拥塞状态。

static inline bool tcp_in_cwnd_reduction(const struct sock *sk)
{return (TCPF_CA_CWR | TCPF_CA_Recovery) &(1 << inet_csk(sk)->icsk_ca_state);
}

如下TCP报文发送函数tcp_write_xmit,如果套接口处于CWR/Recovery拥塞状态,增加prr_out的值。

static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,int push_one, gfp_t gfp)
{...if (likely(sent_pkts)) {if (tcp_in_cwnd_reduction(sk))tp->prr_out += sent_pkts;

如下TCP报文重传函数tcp_xmit_retransmit_queue,如果套接口处于CWR/Recovery拥塞状态,增加prr_out的值。

void tcp_xmit_retransmit_queue(struct sock *sk)
{...rtx_head = tcp_rtx_queue_head(sk);skb = tp->retransmit_skb_hint ?: rtx_head;max_segs = tcp_tso_segs(sk, tcp_current_mss(sk));skb_rbtree_walk_from(skb) {if (tcp_retransmit_skb(sk, skb, segs))return;NET_ADD_STATS(sock_net(sk), mib_idx, tcp_skb_pcount(skb));if (tcp_in_cwnd_reduction(sk))tp->prr_out += tcp_skb_pcount(skb);

内核版本 5.0

TCP快速恢复算法PRR相关推荐

  1. 【RFC6582 TCP快速恢复算法的NewReno修改】(翻译)

    原文 https://datatracker.ietf.org/doc/html/rfc6582  The NewReno Modification to TCP's Fast Recovery Al ...

  2. TCP BBR算法与Reno/CUBIC的对比

    我一再强调,BBR算法是个分界点,所有的TCP拥塞控制算法,被分为BBR之前和BBR之后的(其实发现,这并不是我个人的观点,很多人都这么认为,所有想写本文探个究竟).当然这里的"所有&quo ...

  3. 快速重传与快速恢复算法

    我们认识到在收到一个失序的报文段时,TCP立即需要产生一个ACK(一个重复的ACK).这个重复的ACK不应该被迟延.该重复的ACK的目的在于让对方知道收到一个失序的报文段,并告诉对方自己希望收到的序号 ...

  4. TCP 漕河泾算法(tcp_caohejing)

    题目没意义,要说意义,大致类似于 Vegas,Reno,Tahoe,Westwood,地名. 周三傍晚发一则朋友圈: 总之,名字就是个名字,跟 tcp_vegas,tcp_reno,tcp_tahoe ...

  5. Scalable TCP拥塞算法

    Scalable TCP(STCP)拥塞控制算法,在每个RTT周期内,如果没有发生拥塞,将在接收到每个ACK报文后,将拥塞窗口增加0.01(a值). cwnd = cwnd + 0.01 如果在一个R ...

  6. cubic算法优化_安卓cpu优化tcp拥塞算法cubic和reno怎么选择?

    上述具体的论文可以参考:CUBIC: A New TCP-Friendly High-Speed TCP Variant 1. tcp cubic数学模型 CUBIC在设计上简化了BIC-TCP的窗口 ...

  7. cubic算法优化_安卓cpu优化 tcp拥塞算法cubic和reno怎么选择

    上述具体的论文可以62616964757a686964616fe59b9ee7ad9431333365643662参考:CUBIC: A New TCP-Friendly High-Speed TCP ...

  8. 可以将TCP BBR算法模块化到低版本内核取代锐速吗

    上周的文章引发了比较火爆的争论并带来了争议,我比较满意或者遗憾,尽管如此,如果有人真的能明白在文章的背后我真正想表达的意思,我也就深感欣慰了.还像往常一样,我花周末的时间来总结技术,写点技术散文,同时 ...

  9. 漫谈TCP新算法Elastic-TCP

    自适应的CCA(Congestion Control Algorithm)给人一种简洁明快的感觉. Elastic-TCP是一种新近的算法,比BBR还新,但比BBR简洁多了,可以从Wiki上了解其大概 ...

  10. 令人躁动一时且令人不安的TCP BBR算法

    虽然我在这个周六(2016/12/17)荒废了一天而毫无意义的加班,我依然要在次日把上一周的点滴记录下来.以下在2016/12/18下午19时之前的文章,全属周六通宵之作. 可以说,这个周末过得比较水 ...

最新文章

  1. Eclipse 中导入jar包
  2. 【Raspberry pi】系统安装及基础配置
  3. 诗歌rails之 method_missing
  4. django mysql save_python,django,向mysql更新数据时save()报错不能用
  5. 利用Word将连着一起的字符按照自己指定的”字符串或者字换行“自动换行。
  6. 安卓x86程序安装目录_电脑上的安卓系统体验
  7. HiveJDBC与其他JDBC一起使用时出现java.lang.IllegalArgumentException: Bad URL format
  8. 理解依赖注入(IOC)
  9. 前端学习(496):noscript元素
  10. linux shell转换时间格式,在bash中转换日期格式
  11. request.get_full_path() 和request.path区别
  12. 【Flutter】基础组件【08】BottomNavigationBar
  13. Oracle裁员补偿N+6,员工仍不满意,为何?
  14. 百度地图集成Plist文件需要增加的字段
  15. HDU 4417 Super Mario 主席树
  16. visual studio 2013 编译 filezilla和filezilla server
  17. 爱粤语软件:普通话和粤语转换
  18. 查看自己电脑外网IP
  19. Android Studio报错Using insecure protocols with repositories
  20. C#加密和解密PDF文件

热门文章

  1. VOIP 语音视频通话 ---总述
  2. Flask Marshmallow基本使用
  3. [渝粤题库]西北工业大学中国古代法制史
  4. [音乐推荐]水木年华 - 借我一生
  5. Unity3D游戏开发引擎的产品特点
  6. (未解决)SpringMVC学习——为什么网址不是locahost而是desktop-nottqjs(如图)
  7. 关于Python列表解析式以及初始化指定size列表
  8. Noah-MP陆面过程模型建模方法与站点、区域模拟实践技术应用
  9. android 渠道排名,安卓APP渠道效果统计***排名
  10. ONGene:基于文献检索的肿瘤基因数据库