slow-start-ack-per-xpkt test
// Test of slow start when not application-limited, so that // the cwnd continues to grow. // In this variant, the receiver sends one ACK per 4 packets. // Set up config. To keep things simple, disable the // mechanism that defers sending in order to send bigger TSO packets. `../common/defaults.sh sysctl -q net.ipv4.tcp_tso_win_divisor=100` 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 +0 bind(3, ..., ...) = 0 +0 listen(3, 1) = 0 +.1 < S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7> +0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 8> +.1 < . 1:1(0) ack 1 win 257 +0 accept(3, ..., ...) = 4 +0 setsockopt(4, SOL_SOCKET, SO_SNDBUF, [200000], 4) = 0 +0 write(4, ..., 30000) = 30000 +0 > P. 1:10001(10000) ack 1 +0 %{ assert tcpi_snd_cwnd == 10, tcpi_snd_cwnd }% +.11 < . 1:1(0) ack 4001 win 257 +0 > P. 10001:18001(8000) ack 1 +0 %{ print("ack 4001:", "tcpi_snd_cwnd:", tcpi_snd_cwnd, "tcpi_snd_ssthresh", tcpi_snd_ssthresh) }% +.01 < . 1:1(0) ack 8001 win 257 +0 > P. 18001:26001(8000) ack 1 +0 %{ print("ack 8001:", "tcpi_snd_cwnd:", tcpi_snd_cwnd, "tcpi_snd_ssthresh", tcpi_snd_ssthresh) }% +.005 < . 1:1(0) ack 10001 win 257 +0 > P. 26001:30001(4000) ack 1 +0 %{ print("ack 10001:", "tcpi_snd_cwnd:", tcpi_snd_cwnd, "tcpi_snd_ssthresh", tcpi_snd_ssthresh) }% +0 %{ assert tcpi_snd_cwnd == 20, tcpi_snd_cwnd }%

根据tcp_ack 中的代码可知:ack了新的数据则tcp_may_raise_cwnd ,也就是最后调用tcp_cong_avoid
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); /*它会尝试调用icsk->icsk_ca_ops中注册的拥塞控制函数, 如果没有注册则采用默认策略:使用传统的窗口减小tcp_cwnd_reduction和增大tcp_cwnd_reduction函数。*/ if (icsk->icsk_ca_ops->cong_control) {//目前bbr使用 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); } else if (tcp_may_raise_cwnd(sk, flag)) { /* Advance cwnd if state allows */ tcp_cong_avoid(sk, ack, acked_sacked); } tcp_update_pacing_rate(sk); }
对于cubic 算法
static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked) { struct tcp_sock *tp = tcp_sk(sk); struct bictcp *ca = inet_csk_ca(sk); if (!tcp_is_cwnd_limited(sk)) return; if (tcp_in_slow_start(tp)) { if (hystart && after(ack, ca->end_seq)) bictcp_hystart_reset(sk); acked = tcp_slow_start(tp, acked); if (!acked) return; } bictcp_update(ca, tp->snd_cwnd, acked); tcp_cong_avoid_ai(tp, ca->cnt, acked); }
/* Slow start is used when congestion window is no greater than the slow start * threshold. We base on RFC2581 and also handle stretch ACKs properly. * We do not implement RFC3465 Appropriate Byte Counting (ABC) per se but * something better;) a packet is only considered (s)acked in its entirety to * defend the ACK attacks described in the RFC. Slow start processes a stretch * ACK of degree N as if N acks of degree 1 are received back to back except * ABC caps N to 2. Slow start exits when cwnd grows over ssthresh and * returns the leftover acks to adjust cwnd in congestion avoidance mode. */ u32 tcp_slow_start(struct tcp_sock *tp, u32 acked) { u32 cwnd = min(tp->snd_cwnd + acked, tp->snd_ssthresh); acked -= cwnd - tp->snd_cwnd; tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp); return acked; }
/* We follow the spirit of RFC2861 to validate cwnd but implement a more * flexible approach. The RFC suggests cwnd should not be raised unless * it was fully used previously. And that's exactly what we do in * congestion avoidance mode. But in slow start we allow cwnd to grow * as long as the application has used half the cwnd. * Example : * cwnd is 10 (IW10), but application sends 9 frames. * We allow cwnd to reach 18 when all frames are ACKed. * This check is safe because it's as aggressive as slow start which already * risks 100% overshoot. The advantage is that we discourage application to * either send more filler packets or data to artificially blow up the cwnd * usage, and allow application-limited process to probe bw more aggressively. */ static inline bool tcp_is_cwnd_limited(const struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); /* If in slow start, ensure cwnd grows to twice what was ACKed. */ if (tcp_in_slow_start(tp)) return tp->snd_cwnd < 2 * tp->max_packets_out; return tp->is_cwnd_limited; }
初始化:snd_ssthresh = 0x7ffffff snd_cwnd_clamp = 0xfffffff;
所以可以看到:每次ack几个pkt,snd_cwnd就增长几个
// Test of slow start when not application-limited, so that // the cwnd continues to grow. // In this variant, the receiver ACKs every other packet, // approximating standard delayed ACKs. // Set up config. To keep things simple, disable the // mechanism that defers sending in order to send bigger TSO packets. `../common/defaults.sh sysctl -q net.ipv4.tcp_tso_win_divisor=100` 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 +0 bind(3, ..., ...) = 0 +0 listen(3, 1) = 0 +.1 < S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7> +0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 8> +.1 < . 1:1(0) ack 1 win 257 +0 accept(3, ..., ...) = 4 +0 setsockopt(4, SOL_SOCKET, SO_SNDBUF, [200000], 4) = 0 +0 write(4, ..., 30000) = 30000 +0 > P. 1:10001(10000) ack 1 +0 %{ assert tcpi_snd_cwnd == 10, tcpi_snd_cwnd }% +.105 < . 1:1(0) ack 2001 win 257 +0 > P. 10001:14001(4000) ack 1 +0 %{ print("ack 2001:", "tcpi_snd_cwnd:", tcpi_snd_cwnd, "tcpi_snd_ssthresh", tcpi_snd_ssthresh) }% +.005 < . 1:1(0) ack 4001 win 257 +0 > P. 14001:18001(4000) ack 1 +0 %{ print("ack 4001:", "tcpi_snd_cwnd:", tcpi_snd_cwnd, "tcpi_snd_ssthresh", tcpi_snd_ssthresh) }% +.005 < . 1:1(0) ack 6001 win 257 +0 > P. 18001:22001(4000) ack 1 +0 %{ print("ack 6001:", "tcpi_snd_cwnd:", tcpi_snd_cwnd, "tcpi_snd_ssthresh", tcpi_snd_ssthresh) }% +.005 < . 1:1(0) ack 8001 win 257 +0 > P. 22001:26001(4000) ack 1 +0 %{ print("ack 8001:", "tcpi_snd_cwnd:", tcpi_snd_cwnd, "tcpi_snd_ssthresh", tcpi_snd_ssthresh) }% +.005 < . 1:1(0) ack 10001 win 257 +0 > P. 26001:30001(4000) ack 1 +0 %{ print("ack 10001:", "tcpi_snd_cwnd:", tcpi_snd_cwnd, "tcpi_snd_ssthresh", tcpi_snd_ssthresh) }% +0 %{ assert tcpi_snd_cwnd == 20, tcpi_snd_cwnd }%

但是如果是如果send pkt只有5呢?
// Set up config. `../common/defaults.sh` 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 +0 bind(3, ..., ...) = 0 +0 listen(3, 1) = 0 +0 < S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7> +0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 8> +0 < . 1:1(0) ack 1 win 257 +0 accept(3, ..., ...) = 4 // Only send 5 packets. +0 write(4, ..., 5000) = 5000 +0 > P. 1:5001(5000) ack 1 +0 %{ assert tcpi_snd_cwnd == 10, tcpi_snd_cwnd }% +0 < . 1:1(0) ack 2001 win 257 +0 %{ assert tcpi_snd_cwnd == 10, 'cwnd=%d' % tcpi_snd_cwnd }% +0 < . 1:1(0) ack 4001 win 257 +0 %{ assert tcpi_snd_cwnd == 10, 'cwnd=%d' % tcpi_snd_cwnd }% +0 < . 1:1(0) ack 5001 win 257 +0 %{ assert tcpi_snd_cwnd == 10, 'cwnd=%d' % tcpi_snd_cwnd }%
也就是cwnd不会增长,原因就是 tcp_is_cwnd_limited 的判断方法!!
/* If in slow start, ensure cwnd grows to twice what was ACKed. */ if (tcp_in_slow_start(tp)) return tp->snd_cwnd < 2 * tp->max_packets_out;
也就是如果tp->snd_cwnd >= 2 * tp->max_packets_out; 此时认为是 app_limitedm,则不会进行拥塞调整。
所以下面例子;如果send 7 pkt,当ack了4pkt后, cwnd增长到14, 即使后面ack了5001 6001 7001,由于此时cwnd >= pkt_out*2 ;不会调整cwnd
// Set up config. `../common/defaults.sh` 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 +0 bind(3, ..., ...) = 0 +0 listen(3, 1) = 0 +0 < S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7> +0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 8> +0 < . 1:1(0) ack 1 win 257 +0 accept(3, ..., ...) = 4 // Only send 5 packets. +0 write(4, ..., 7000) = 7000 +0 > P. 1:7001(7000) ack 1 +0 %{ assert tcpi_snd_cwnd == 10, tcpi_snd_cwnd }% +0 %{ print("ack 0:", "tcpi_snd_cwnd:", tcpi_snd_cwnd) }% +0 < . 1:1(0) ack 2001 win 257 +0 %{ print("ack 2:", "tcpi_snd_cwnd:", tcpi_snd_cwnd) }% +0 < . 1:1(0) ack 3001 win 257 +0 %{ print("ack 3:", "tcpi_snd_cwnd:", tcpi_snd_cwnd) }% +0 < . 1:1(0) ack 4001 win 257 +0 %{ print("ack 4:", "tcpi_snd_cwnd:", tcpi_snd_cwnd) }% +0 < . 1:1(0) ack 5001 win 257 +0 %{ print("ack 5:", "tcpi_snd_cwnd:", tcpi_snd_cwnd) }% +0 < . 1:1(0) ack 7001 win 257 +0 %{ print("ack 7:", "tcpi_snd_cwnd:", tcpi_snd_cwnd) }% // +0 `sleep 1` /* ack 0: tcpi_snd_cwnd: 10 ack 2: tcpi_snd_cwnd: 12 ack 3: tcpi_snd_cwnd: 13 ack 4: tcpi_snd_cwnd: 14 ack 5: tcpi_snd_cwnd: 14 ack 7: tcpi_snd_cwnd: 14 */
再看一个样例:
+0 write(4, ..., 8000) = 8000 +0 > P. 1:8001(8000) ack 1 +0 %{ assert tcpi_snd_cwnd == 10, tcpi_snd_cwnd }% +0 %{ print("ack 0:", "tcpi_snd_cwnd:", tcpi_snd_cwnd) }% +0 < . 1:1(0) ack 2001 win 257 +0 %{ print("ack 2:", "tcpi_snd_cwnd:", tcpi_snd_cwnd) }% +0 < . 1:1(0) ack 3001 win 257 +0 %{ print("ack 3:", "tcpi_snd_cwnd:", tcpi_snd_cwnd) }% +0 < . 1:1(0) ack 4001 win 257 +0 %{ print("ack 4:", "tcpi_snd_cwnd:", tcpi_snd_cwnd) }% +0 < . 1:1(0) ack 5001 win 257 +0 %{ print("ack 5:", "tcpi_snd_cwnd:", tcpi_snd_cwnd) }% +0 < . 1:1(0) ack 6001 win 257 // 屏蔽和不屏蔽掉此语句对比 +0 %{ print("ack 6:", "tcpi_snd_cwnd:", tcpi_snd_cwnd) }% +0 < . 1:1(0) ack 7001 win 257 +0 %{ print("ack 7:", "tcpi_snd_cwnd:", tcpi_snd_cwnd) }% +0 < . 1:1(0) ack 8001 win 257 +0 %{ print("ack 8:", "tcpi_snd_cwnd:", tcpi_snd_cwnd) }% // +0 `sleep 1`
如果屏蔽掉+0 < . 1:1(0) ack 6001 win 257

可以看出此时 ack 7 时 从cwnd达到最大10+7 >=2*8
如果执行+0 < . 1:1(0) ack 6001 win 257

可以看出此时 ack 6 时 从cwnd达到最大10+6 >= 2*8
http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!!
但行好事 莫问前程
--身高体重180的胖子

浙公网安备 33010602011771号