xref: /openbmc/linux/net/netfilter/ipvs/ip_vs_est.c (revision 2a0751af)
1 /*
2  * ip_vs_est.c: simple rate estimator for IPVS
3  *
4  * Authors:     Wensong Zhang <wensong@linuxvirtualserver.org>
5  *
6  *              This program is free software; you can redistribute it and/or
7  *              modify it under the terms of the GNU General Public License
8  *              as published by the Free Software Foundation; either version
9  *              2 of the License, or (at your option) any later version.
10  *
11  * Changes:     Hans Schillstrom <hans.schillstrom@ericsson.com>
12  *              Network name space (netns) aware.
13  *              Global data moved to netns i.e struct netns_ipvs
14  *              Affected data: est_list and est_lock.
15  *              estimation_timer() runs with timer per netns.
16  *              get_stats()) do the per cpu summing.
17  */
18 
19 #define KMSG_COMPONENT "IPVS"
20 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
21 
22 #include <linux/kernel.h>
23 #include <linux/jiffies.h>
24 #include <linux/types.h>
25 #include <linux/interrupt.h>
26 #include <linux/sysctl.h>
27 #include <linux/list.h>
28 
29 #include <net/ip_vs.h>
30 
31 /*
32   This code is to estimate rate in a shorter interval (such as 8
33   seconds) for virtual services and real servers. For measure rate in a
34   long interval, it is easy to implement a user level daemon which
35   periodically reads those statistical counters and measure rate.
36 
37   Currently, the measurement is activated by slow timer handler. Hope
38   this measurement will not introduce too much load.
39 
40   We measure rate during the last 8 seconds every 2 seconds:
41 
42     avgrate = avgrate*(1-W) + rate*W
43 
44     where W = 2^(-2)
45 
46   NOTES.
47 
48   * The stored value for average bps is scaled by 2^5, so that maximal
49     rate is ~2.15Gbits/s, average pps and cps are scaled by 2^10.
50 
51   * A lot code is taken from net/sched/estimator.c
52  */
53 
54 
55 /*
56  * Make a summary from each cpu
57  */
58 static void ip_vs_read_cpu_stats(struct ip_vs_stats_user *sum,
59 				 struct ip_vs_cpu_stats *stats)
60 {
61 	int i;
62 
63 	for_each_possible_cpu(i) {
64 		struct ip_vs_cpu_stats *s = per_cpu_ptr(stats, i);
65 		unsigned int start;
66 		__u64 inbytes, outbytes;
67 		if (i) {
68 			sum->conns += s->ustats.conns;
69 			sum->inpkts += s->ustats.inpkts;
70 			sum->outpkts += s->ustats.outpkts;
71 			do {
72 				start = u64_stats_fetch_begin(&s->syncp);
73 				inbytes = s->ustats.inbytes;
74 				outbytes = s->ustats.outbytes;
75 			} while (u64_stats_fetch_retry(&s->syncp, start));
76 			sum->inbytes += inbytes;
77 			sum->outbytes += outbytes;
78 		} else {
79 			sum->conns = s->ustats.conns;
80 			sum->inpkts = s->ustats.inpkts;
81 			sum->outpkts = s->ustats.outpkts;
82 			do {
83 				start = u64_stats_fetch_begin(&s->syncp);
84 				sum->inbytes = s->ustats.inbytes;
85 				sum->outbytes = s->ustats.outbytes;
86 			} while (u64_stats_fetch_retry(&s->syncp, start));
87 		}
88 	}
89 }
90 
91 
92 static void estimation_timer(unsigned long arg)
93 {
94 	struct ip_vs_estimator *e;
95 	struct ip_vs_stats *s;
96 	u32 n_conns;
97 	u32 n_inpkts, n_outpkts;
98 	u64 n_inbytes, n_outbytes;
99 	u32 rate;
100 	struct net *net = (struct net *)arg;
101 	struct netns_ipvs *ipvs;
102 
103 	ipvs = net_ipvs(net);
104 	spin_lock(&ipvs->est_lock);
105 	list_for_each_entry(e, &ipvs->est_list, list) {
106 		s = container_of(e, struct ip_vs_stats, est);
107 
108 		spin_lock(&s->lock);
109 		ip_vs_read_cpu_stats(&s->ustats, s->cpustats);
110 		n_conns = s->ustats.conns;
111 		n_inpkts = s->ustats.inpkts;
112 		n_outpkts = s->ustats.outpkts;
113 		n_inbytes = s->ustats.inbytes;
114 		n_outbytes = s->ustats.outbytes;
115 
116 		/* scaled by 2^10, but divided 2 seconds */
117 		rate = (n_conns - e->last_conns) << 9;
118 		e->last_conns = n_conns;
119 		e->cps += ((long)rate - (long)e->cps) >> 2;
120 		s->ustats.cps = (e->cps + 0x1FF) >> 10;
121 
122 		rate = (n_inpkts - e->last_inpkts) << 9;
123 		e->last_inpkts = n_inpkts;
124 		e->inpps += ((long)rate - (long)e->inpps) >> 2;
125 		s->ustats.inpps = (e->inpps + 0x1FF) >> 10;
126 
127 		rate = (n_outpkts - e->last_outpkts) << 9;
128 		e->last_outpkts = n_outpkts;
129 		e->outpps += ((long)rate - (long)e->outpps) >> 2;
130 		s->ustats.outpps = (e->outpps + 0x1FF) >> 10;
131 
132 		rate = (n_inbytes - e->last_inbytes) << 4;
133 		e->last_inbytes = n_inbytes;
134 		e->inbps += ((long)rate - (long)e->inbps) >> 2;
135 		s->ustats.inbps = (e->inbps + 0xF) >> 5;
136 
137 		rate = (n_outbytes - e->last_outbytes) << 4;
138 		e->last_outbytes = n_outbytes;
139 		e->outbps += ((long)rate - (long)e->outbps) >> 2;
140 		s->ustats.outbps = (e->outbps + 0xF) >> 5;
141 		spin_unlock(&s->lock);
142 	}
143 	spin_unlock(&ipvs->est_lock);
144 	mod_timer(&ipvs->est_timer, jiffies + 2*HZ);
145 }
146 
147 void ip_vs_new_estimator(struct net *net, struct ip_vs_stats *stats)
148 {
149 	struct netns_ipvs *ipvs = net_ipvs(net);
150 	struct ip_vs_estimator *est = &stats->est;
151 
152 	INIT_LIST_HEAD(&est->list);
153 
154 	est->last_conns = stats->ustats.conns;
155 	est->cps = stats->ustats.cps<<10;
156 
157 	est->last_inpkts = stats->ustats.inpkts;
158 	est->inpps = stats->ustats.inpps<<10;
159 
160 	est->last_outpkts = stats->ustats.outpkts;
161 	est->outpps = stats->ustats.outpps<<10;
162 
163 	est->last_inbytes = stats->ustats.inbytes;
164 	est->inbps = stats->ustats.inbps<<5;
165 
166 	est->last_outbytes = stats->ustats.outbytes;
167 	est->outbps = stats->ustats.outbps<<5;
168 
169 	spin_lock_bh(&ipvs->est_lock);
170 	list_add(&est->list, &ipvs->est_list);
171 	spin_unlock_bh(&ipvs->est_lock);
172 }
173 
174 void ip_vs_kill_estimator(struct net *net, struct ip_vs_stats *stats)
175 {
176 	struct netns_ipvs *ipvs = net_ipvs(net);
177 	struct ip_vs_estimator *est = &stats->est;
178 
179 	spin_lock_bh(&ipvs->est_lock);
180 	list_del(&est->list);
181 	spin_unlock_bh(&ipvs->est_lock);
182 }
183 
184 void ip_vs_zero_estimator(struct ip_vs_stats *stats)
185 {
186 	struct ip_vs_estimator *est = &stats->est;
187 
188 	/* set counters zero, caller must hold the stats->lock lock */
189 	est->last_inbytes = 0;
190 	est->last_outbytes = 0;
191 	est->last_conns = 0;
192 	est->last_inpkts = 0;
193 	est->last_outpkts = 0;
194 	est->cps = 0;
195 	est->inpps = 0;
196 	est->outpps = 0;
197 	est->inbps = 0;
198 	est->outbps = 0;
199 }
200 
201 static int __net_init __ip_vs_estimator_init(struct net *net)
202 {
203 	struct netns_ipvs *ipvs = net_ipvs(net);
204 
205 	INIT_LIST_HEAD(&ipvs->est_list);
206 	spin_lock_init(&ipvs->est_lock);
207 	setup_timer(&ipvs->est_timer, estimation_timer, (unsigned long)net);
208 	mod_timer(&ipvs->est_timer, jiffies + 2 * HZ);
209 	return 0;
210 }
211 
212 static void __net_exit __ip_vs_estimator_exit(struct net *net)
213 {
214 	del_timer_sync(&net_ipvs(net)->est_timer);
215 }
216 static struct pernet_operations ip_vs_app_ops = {
217 	.init = __ip_vs_estimator_init,
218 	.exit = __ip_vs_estimator_exit,
219 };
220 
221 int __init ip_vs_estimator_init(void)
222 {
223 	int rv;
224 
225 	rv = register_pernet_subsys(&ip_vs_app_ops);
226 	return rv;
227 }
228 
229 void ip_vs_estimator_cleanup(void)
230 {
231 	unregister_pernet_subsys(&ip_vs_app_ops);
232 }
233