nuttx/net/tcp/tcp_cc.c
liqinhui f61dc72892 net/tcp:Add NewReno congestion control.
- NewReno congestion control algorithm is used to solve the problem
  of network congestion breakdown. NewReno congestion control includes
  slow start, collision avoidance, fast retransmission, and fast
  recovery. The implementation refers to RFC6582 and RFC5681.

- In addition, we optimize the congestion algorithm. In the conflict
  avoidance stage, the maximum congestion window max_cwnd is used to
  limit the excessive growth of cwnd and prevent network jitter
  caused by congestion. Maximum congestion window max_cwnd is updated
  with the current congestion window cwnd and the update weight is
  0.875 when an RTO timeout occurs.

Signed-off-by: liqinhui <liqinhui@xiaomi.com>
2023-05-16 12:35:01 -03:00

302 lines
9.0 KiB
C

/****************************************************************************
* 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);
}
}
}
}