123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- // SPDX-License-Identifier: GPL-2.0
- /* RTT/RTO calculation.
- *
- * Adapted from TCP for AF_RXRPC by David Howells ([email protected])
- *
- * https://tools.ietf.org/html/rfc6298
- * https://tools.ietf.org/html/rfc1122#section-4.2.3.1
- * http://ccr.sigcomm.org/archive/1995/jan95/ccr-9501-partridge87.pdf
- */
- #include <linux/net.h>
- #include "ar-internal.h"
- #define RXRPC_RTO_MAX ((unsigned)(120 * HZ))
- #define RXRPC_TIMEOUT_INIT ((unsigned)(1*HZ)) /* RFC6298 2.1 initial RTO value */
- #define rxrpc_jiffies32 ((u32)jiffies) /* As rxrpc_jiffies32 */
- static u32 rxrpc_rto_min_us(struct rxrpc_peer *peer)
- {
- return 200;
- }
- static u32 __rxrpc_set_rto(const struct rxrpc_peer *peer)
- {
- return usecs_to_jiffies((peer->srtt_us >> 3) + peer->rttvar_us);
- }
- static u32 rxrpc_bound_rto(u32 rto)
- {
- return min(rto, RXRPC_RTO_MAX);
- }
- /*
- * Called to compute a smoothed rtt estimate. The data fed to this
- * routine either comes from timestamps, or from segments that were
- * known _not_ to have been retransmitted [see Karn/Partridge
- * Proceedings SIGCOMM 87]. The algorithm is from the SIGCOMM 88
- * piece by Van Jacobson.
- * NOTE: the next three routines used to be one big routine.
- * To save cycles in the RFC 1323 implementation it was better to break
- * it up into three procedures. -- erics
- */
- static void rxrpc_rtt_estimator(struct rxrpc_peer *peer, long sample_rtt_us)
- {
- long m = sample_rtt_us; /* RTT */
- u32 srtt = peer->srtt_us;
- /* The following amusing code comes from Jacobson's
- * article in SIGCOMM '88. Note that rtt and mdev
- * are scaled versions of rtt and mean deviation.
- * This is designed to be as fast as possible
- * m stands for "measurement".
- *
- * On a 1990 paper the rto value is changed to:
- * RTO = rtt + 4 * mdev
- *
- * Funny. This algorithm seems to be very broken.
- * These formulae increase RTO, when it should be decreased, increase
- * too slowly, when it should be increased quickly, decrease too quickly
- * etc. I guess in BSD RTO takes ONE value, so that it is absolutely
- * does not matter how to _calculate_ it. Seems, it was trap
- * that VJ failed to avoid. 8)
- */
- if (srtt != 0) {
- m -= (srtt >> 3); /* m is now error in rtt est */
- srtt += m; /* rtt = 7/8 rtt + 1/8 new */
- if (m < 0) {
- m = -m; /* m is now abs(error) */
- m -= (peer->mdev_us >> 2); /* similar update on mdev */
- /* This is similar to one of Eifel findings.
- * Eifel blocks mdev updates when rtt decreases.
- * This solution is a bit different: we use finer gain
- * for mdev in this case (alpha*beta).
- * Like Eifel it also prevents growth of rto,
- * but also it limits too fast rto decreases,
- * happening in pure Eifel.
- */
- if (m > 0)
- m >>= 3;
- } else {
- m -= (peer->mdev_us >> 2); /* similar update on mdev */
- }
- peer->mdev_us += m; /* mdev = 3/4 mdev + 1/4 new */
- if (peer->mdev_us > peer->mdev_max_us) {
- peer->mdev_max_us = peer->mdev_us;
- if (peer->mdev_max_us > peer->rttvar_us)
- peer->rttvar_us = peer->mdev_max_us;
- }
- } else {
- /* no previous measure. */
- srtt = m << 3; /* take the measured time to be rtt */
- peer->mdev_us = m << 1; /* make sure rto = 3*rtt */
- peer->rttvar_us = max(peer->mdev_us, rxrpc_rto_min_us(peer));
- peer->mdev_max_us = peer->rttvar_us;
- }
- peer->srtt_us = max(1U, srtt);
- }
- /*
- * Calculate rto without backoff. This is the second half of Van Jacobson's
- * routine referred to above.
- */
- static void rxrpc_set_rto(struct rxrpc_peer *peer)
- {
- u32 rto;
- /* 1. If rtt variance happened to be less 50msec, it is hallucination.
- * It cannot be less due to utterly erratic ACK generation made
- * at least by solaris and freebsd. "Erratic ACKs" has _nothing_
- * to do with delayed acks, because at cwnd>2 true delack timeout
- * is invisible. Actually, Linux-2.4 also generates erratic
- * ACKs in some circumstances.
- */
- rto = __rxrpc_set_rto(peer);
- /* 2. Fixups made earlier cannot be right.
- * If we do not estimate RTO correctly without them,
- * all the algo is pure shit and should be replaced
- * with correct one. It is exactly, which we pretend to do.
- */
- /* NOTE: clamping at RXRPC_RTO_MIN is not required, current algo
- * guarantees that rto is higher.
- */
- peer->rto_j = rxrpc_bound_rto(rto);
- }
- static void rxrpc_ack_update_rtt(struct rxrpc_peer *peer, long rtt_us)
- {
- if (rtt_us < 0)
- return;
- //rxrpc_update_rtt_min(peer, rtt_us);
- rxrpc_rtt_estimator(peer, rtt_us);
- rxrpc_set_rto(peer);
- /* RFC6298: only reset backoff on valid RTT measurement. */
- peer->backoff = 0;
- }
- /*
- * Add RTT information to cache. This is called in softirq mode and has
- * exclusive access to the peer RTT data.
- */
- void rxrpc_peer_add_rtt(struct rxrpc_call *call, enum rxrpc_rtt_rx_trace why,
- int rtt_slot,
- rxrpc_serial_t send_serial, rxrpc_serial_t resp_serial,
- ktime_t send_time, ktime_t resp_time)
- {
- struct rxrpc_peer *peer = call->peer;
- s64 rtt_us;
- rtt_us = ktime_to_us(ktime_sub(resp_time, send_time));
- if (rtt_us < 0)
- return;
- spin_lock(&peer->rtt_input_lock);
- rxrpc_ack_update_rtt(peer, rtt_us);
- if (peer->rtt_count < 3)
- peer->rtt_count++;
- spin_unlock(&peer->rtt_input_lock);
- trace_rxrpc_rtt_rx(call, why, rtt_slot, send_serial, resp_serial,
- peer->srtt_us >> 3, peer->rto_j);
- }
- /*
- * Get the retransmission timeout to set in jiffies, backing it off each time
- * we retransmit.
- */
- unsigned long rxrpc_get_rto_backoff(struct rxrpc_peer *peer, bool retrans)
- {
- u64 timo_j;
- u8 backoff = READ_ONCE(peer->backoff);
- timo_j = peer->rto_j;
- timo_j <<= backoff;
- if (retrans && timo_j * 2 <= RXRPC_RTO_MAX)
- WRITE_ONCE(peer->backoff, backoff + 1);
- if (timo_j < 1)
- timo_j = 1;
- return timo_j;
- }
- void rxrpc_peer_init_rtt(struct rxrpc_peer *peer)
- {
- peer->rto_j = RXRPC_TIMEOUT_INIT;
- peer->mdev_us = jiffies_to_usecs(RXRPC_TIMEOUT_INIT);
- peer->backoff = 0;
- //minmax_reset(&peer->rtt_min, rxrpc_jiffies32, ~0U);
- }
|