forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
195 lines
5.8 KiB
195 lines
5.8 KiB
// 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); |
|
}
|
|
|