| /**************************************************************************** |
| * net/tcp/tcp_cc.c |
| * Handling TCP congestion control |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <debug.h> |
| |
| #include "tcp/tcp.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| #define TCP_IPV4_DEFAULT_MSS 536 |
| |
| /* Initial Window threshold constants */ |
| |
| #define IW_MAX 4380 /* Initial Window maximum */ |
| #define IW_MAX_HALF 2190 |
| #define IW_MAX_QUATER 1095 |
| |
| /* Calculate the Initial Window, also used as Restart Window |
| * RFC5681 Section 3.1 specifies the default conservative values. |
| */ |
| |
| #define CC_INIT_CWND(cwnd, mss) \ |
| do { \ |
| if ((mss) > IW_MAX_HALF) \ |
| { \ |
| (cwnd) = 2 * (mss); \ |
| } \ |
| else if ((mss) > IW_MAX_QUATER) \ |
| { \ |
| (cwnd) = 3 * (mss); \ |
| } \ |
| else \ |
| { \ |
| (cwnd) = 4 * (mss); \ |
| } \ |
| } while(0) |
| |
| /* Increments a size inc and holds at max value rather than rollover. */ |
| |
| #define CC_CWND_INC(wnd, inc) \ |
| do { \ |
| if ((uint32_t)((wnd) + (inc)) >= (wnd)) \ |
| { \ |
| (wnd) = (uint32_t)((wnd) + (inc)); \ |
| } \ |
| else \ |
| { \ |
| (wnd) = (uint32_t)-1; \ |
| } \ |
| } while(0) |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: tcp_cc_init |
| * |
| * Description: |
| * Initialize the congestion control variables, cwnd, ssthresh and dupacks. |
| * The function is called on starting a new connection. |
| * |
| * Input Parameters: |
| * conn - The TCP connection of interest |
| * |
| * Returned Value: |
| * None |
| * |
| * Assumptions: |
| * The normal user level code is calling the connect/accept to start a new |
| * connection. |
| * |
| ****************************************************************************/ |
| |
| void tcp_cc_init(FAR struct tcp_conn_s *conn) |
| { |
| CC_INIT_CWND(conn->cwnd, conn->mss); |
| |
| /* RFC 5681 recommends setting ssthresh arbitrarily high and |
| * gives an example of using the largest advertised receive window. |
| * We've seen complications with receiving TCPs that use window |
| * scaling and/or window auto-tuning where the initial advertised |
| * window is very small and then grows rapidly once the connection |
| * is established. To avoid these complications, we set ssthresh to |
| * the largest effective cwnd (amount of in-flight data) that the |
| * sender can have. |
| */ |
| |
| conn->ssthresh = 2 * TCP_IPV4_DEFAULT_MSS; |
| conn->dupacks = 0; |
| } |
| |
| /**************************************************************************** |
| * Name: tcp_cc_update |
| * |
| * Description: |
| * Update the congestion control variables when recieve the SYNACK/ACK |
| * packet from the peer in the connection phase. |
| * |
| * Input Parameters: |
| * conn - The TCP connection of interest |
| * tcp - The TCP header. |
| * |
| * Returned Value: |
| * None |
| * |
| * Assumptions: |
| * The network is locked. |
| * |
| ****************************************************************************/ |
| |
| void tcp_cc_update(FAR struct tcp_conn_s *conn, FAR struct tcp_hdr_s *tcp) |
| { |
| /* After Fast retransmitted, set ssthresh to the maximum of |
| * the unacked and the 2*SMSS, and enter to Fast Recovery. |
| * ssthresh = max (FlightSize / 2, 2*SMSS) referring to rfc5681 |
| * cwnd=ssthresh + 3*SMSS referring to rfc5681 |
| */ |
| |
| if (conn->flags & TCP_INFT) |
| { |
| conn->ssthresh = MAX(conn->tx_unacked / 2, 2 * conn->mss); |
| conn->cwnd = conn->ssthresh + 3 * conn->mss; |
| |
| conn->flags &= ~TCP_INFT; |
| conn->flags |= TCP_INFR; |
| } |
| |
| /* Update the cc parameters in the TCP_SYN_RCVD and TCP_SYN_SENT states |
| * when the tcp connection is established. |
| */ |
| |
| else |
| { |
| conn->last_ackno = tcp_getsequence(tcp->ackno); |
| CC_INIT_CWND(conn->cwnd, conn->mss); |
| conn->max_cwnd = conn->snd_wnd; |
| conn->ssthresh = MAX(conn->snd_wnd, conn->ssthresh); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: tcp_cc_recv_ack |
| * |
| * Description: |
| * Update congestion control variables |
| * |
| * Input Parameters: |
| * conn - The TCP connection of interest |
| * tcp - The TCP header. |
| * |
| * Returned Value: |
| * None |
| * |
| * Assumptions: |
| * The network is locked. |
| * |
| ****************************************************************************/ |
| |
| void tcp_cc_recv_ack(FAR struct tcp_conn_s *conn, FAR struct tcp_hdr_s *tcp) |
| { |
| uint32_t ackno = tcp_getsequence(tcp->ackno); |
| |
| /* Its only a duplicate ack if: |
| * 1) It doesn't ACK new data |
| * 2) There is outstanding unacknowledged data (retransmission |
| * timer running) |
| * 3) The ACK is == biggest ACK sequence number so far (last_ackno) |
| * |
| * If it passes all conditions, should process as a dupack: |
| * a) dupacks < 3: do nothing |
| * b) dupacks == 3: fast retransmit |
| * c) dupacks > 3: increase cwnd |
| * |
| * If ackno is between last_ackno and snd_seq, should reset dupack counter. |
| */ |
| |
| /* Clause 1 */ |
| |
| if (TCP_SEQ_LTE(ackno, conn->last_ackno)) |
| { |
| /* Clause 2 and Clause 3 */ |
| |
| if (conn->timer >= 0 && |
| conn->last_ackno == ackno) |
| { |
| if (++conn->dupacks > TCP_FAST_RETRANSMISSION_THRESH) |
| { |
| /* Inflate the congestion window */ |
| |
| CC_CWND_INC(conn->cwnd, conn->mss); |
| } |
| |
| if (conn->dupacks >= TCP_FAST_RETRANSMISSION_THRESH) |
| { |
| /* Do fast retransmit, but it is delayed in |
| * psock_send_eventhandler. Set the TCP_INFT flag. |
| */ |
| |
| conn->flags |= TCP_INFT; |
| conn->fr_recover = tcp_getsequence(conn->sndseq); |
| } |
| } |
| } |
| else if (TCP_SEQ_GT(ackno, conn->last_ackno) && |
| TCP_SEQ_LTE(ackno, tcp_getsequence(conn->sndseq))) |
| { |
| /* We come here when the ACK acknowledges new data. */ |
| |
| uint32_t acked = TCP_SEQ_SUB(ackno, conn->last_ackno); |
| |
| /* Reset dupacks and update last_ackno. */ |
| |
| conn->dupacks = 0; |
| conn->last_ackno = ackno; |
| |
| /* When the ackno covers more than the fr_recover, exit the |
| * fast recovery. Then, reset the "IN Fast Recovery" flags. |
| * Also reset the congestion window to the slow start threshold. |
| * If not, cwnd should be increased by mss. RFC6582. |
| */ |
| |
| if (conn->flags & TCP_INFR) |
| { |
| if (ackno - 1 > conn->fr_recover) |
| { |
| /* Reset the fast retransmit variables. */ |
| |
| conn->flags &= ~TCP_INFR; |
| conn->cwnd = conn->ssthresh; |
| } |
| else |
| { |
| CC_CWND_INC(conn->cwnd, conn->mss); |
| return; |
| } |
| } |
| |
| /* Update the congestion control variables (cwnd and ssthresh). */ |
| |
| if (conn->tcpstateflags >= TCP_ESTABLISHED) |
| { |
| uint32_t increase; |
| |
| if (conn->cwnd < conn->ssthresh) |
| { |
| /* slow start (RFC 5681): |
| * Grow cwnd exponentially by maxseg(smss) per ACK. |
| */ |
| |
| increase = acked > 0 ? MIN(acked, conn->mss) : conn->mss; |
| |
| CC_CWND_INC(conn->cwnd, increase); |
| ninfo("update slow start cwnd to %u\n", conn->cwnd); |
| } |
| else |
| { |
| /* cong avoid (RFC 5681): |
| * Grow cwnd linearly by approximately maxseg per RTT using |
| * maxseg^2 / cwnd per ACK as the increment. |
| * If cwnd > maxseg^2, fix the cwnd increment at 1 byte to |
| * avoid capping cwnd. |
| */ |
| |
| increase = MAX((conn->mss * conn->mss / conn->cwnd), 1); |
| |
| CC_CWND_INC(conn->cwnd, increase); |
| conn->cwnd = MIN(conn->cwnd, conn->max_cwnd); |
| ninfo("update congestion avoidance cwnd to %u\n", conn->cwnd); |
| } |
| } |
| } |
| } |