1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * linux/net/sunrpc/timer.c
4  *
5  * Estimate RPC request round trip time.
6  *
7  * Based on packet round-trip and variance estimator algorithms described
8  * in appendix A of "Congestion Avoidance and Control" by Van Jacobson
9  * and Michael J. Karels (ACM Computer Communication Review; Proceedings
10  * of the Sigcomm '88 Symposium in Stanford, CA, August, 1988).
11  *
12  * This RTT estimator is used only for RPC over datagram protocols.
13  *
14  * Copyright (C) 2002 Trond Myklebust <trond.myklebust@fys.uio.no>
15  */
16 
17 #include <asm/param.h>
18 
19 #include <linux/types.h>
20 #include <linux/unistd.h>
21 #include <linux/module.h>
22 
23 #include <linux/sunrpc/clnt.h>
24 
25 #define RPC_RTO_MAX (60*HZ)
26 #define RPC_RTO_INIT (HZ/5)
27 #define RPC_RTO_MIN (HZ/10)
28 
29 /**
30  * rpc_init_rtt - Initialize an RPC RTT estimator context
31  * @rt: context to initialize
32  * @timeo: initial timeout value, in jiffies
33  *
34  */
rpc_init_rtt(struct rpc_rtt * rt,unsigned long timeo)35 void rpc_init_rtt(struct rpc_rtt *rt, unsigned long timeo)
36 {
37 	unsigned long init = 0;
38 	unsigned int i;
39 
40 	rt->timeo = timeo;
41 
42 	if (timeo > RPC_RTO_INIT)
43 		init = (timeo - RPC_RTO_INIT) << 3;
44 	for (i = 0; i < 5; i++) {
45 		rt->srtt[i] = init;
46 		rt->sdrtt[i] = RPC_RTO_INIT;
47 		rt->ntimeouts[i] = 0;
48 	}
49 }
50 EXPORT_SYMBOL_GPL(rpc_init_rtt);
51 
52 /**
53  * rpc_update_rtt - Update an RPC RTT estimator context
54  * @rt: context to update
55  * @timer: timer array index (request type)
56  * @m: recent actual RTT, in jiffies
57  *
58  * NB: When computing the smoothed RTT and standard deviation,
59  *     be careful not to produce negative intermediate results.
60  */
rpc_update_rtt(struct rpc_rtt * rt,unsigned int timer,long m)61 void rpc_update_rtt(struct rpc_rtt *rt, unsigned int timer, long m)
62 {
63 	long *srtt, *sdrtt;
64 
65 	if (timer-- == 0)
66 		return;
67 
68 	/* jiffies wrapped; ignore this one */
69 	if (m < 0)
70 		return;
71 
72 	if (m == 0)
73 		m = 1L;
74 
75 	srtt = (long *)&rt->srtt[timer];
76 	m -= *srtt >> 3;
77 	*srtt += m;
78 
79 	if (m < 0)
80 		m = -m;
81 
82 	sdrtt = (long *)&rt->sdrtt[timer];
83 	m -= *sdrtt >> 2;
84 	*sdrtt += m;
85 
86 	/* Set lower bound on the variance */
87 	if (*sdrtt < RPC_RTO_MIN)
88 		*sdrtt = RPC_RTO_MIN;
89 }
90 EXPORT_SYMBOL_GPL(rpc_update_rtt);
91 
92 /**
93  * rpc_calc_rto - Provide an estimated timeout value
94  * @rt: context to use for calculation
95  * @timer: timer array index (request type)
96  *
97  * Estimate RTO for an NFS RPC sent via an unreliable datagram.  Use
98  * the mean and mean deviation of RTT for the appropriate type of RPC
99  * for frequently issued RPCs, and a fixed default for the others.
100  *
101  * The justification for doing "other" this way is that these RPCs
102  * happen so infrequently that timer estimation would probably be
103  * stale.  Also, since many of these RPCs are non-idempotent, a
104  * conservative timeout is desired.
105  *
106  * getattr, lookup,
107  * read, write, commit     - A+4D
108  * other                   - timeo
109  */
rpc_calc_rto(struct rpc_rtt * rt,unsigned int timer)110 unsigned long rpc_calc_rto(struct rpc_rtt *rt, unsigned int timer)
111 {
112 	unsigned long res;
113 
114 	if (timer-- == 0)
115 		return rt->timeo;
116 
117 	res = ((rt->srtt[timer] + 7) >> 3) + rt->sdrtt[timer];
118 	if (res > RPC_RTO_MAX)
119 		res = RPC_RTO_MAX;
120 
121 	return res;
122 }
123 EXPORT_SYMBOL_GPL(rpc_calc_rto);
124