xref: /openbmc/linux/net/netfilter/ipvs/ip_vs_est.c (revision d1ee4fea)
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 __percpu *stats)
60 {
61 	int i;
62 	bool add = false;
63 
64 	for_each_possible_cpu(i) {
65 		struct ip_vs_cpu_stats *s = per_cpu_ptr(stats, i);
66 		unsigned int start;
67 		__u64 inbytes, outbytes;
68 		if (add) {
69 			sum->conns += s->ustats.conns;
70 			sum->inpkts += s->ustats.inpkts;
71 			sum->outpkts += s->ustats.outpkts;
72 			do {
73 				start = u64_stats_fetch_begin(&s->syncp);
74 				inbytes = s->ustats.inbytes;
75 				outbytes = s->ustats.outbytes;
76 			} while (u64_stats_fetch_retry(&s->syncp, start));
77 			sum->inbytes += inbytes;
78 			sum->outbytes += outbytes;
79 		} else {
80 			add = true;
81 			sum->conns = s->ustats.conns;
82 			sum->inpkts = s->ustats.inpkts;
83 			sum->outpkts = s->ustats.outpkts;
84 			do {
85 				start = u64_stats_fetch_begin(&s->syncp);
86 				sum->inbytes = s->ustats.inbytes;
87 				sum->outbytes = s->ustats.outbytes;
88 			} while (u64_stats_fetch_retry(&s->syncp, start));
89 		}
90 	}
91 }
92 
93 
94 static void estimation_timer(unsigned long arg)
95 {
96 	struct ip_vs_estimator *e;
97 	struct ip_vs_stats *s;
98 	u32 n_conns;
99 	u32 n_inpkts, n_outpkts;
100 	u64 n_inbytes, n_outbytes;
101 	u32 rate;
102 	struct net *net = (struct net *)arg;
103 	struct netns_ipvs *ipvs;
104 
105 	ipvs = net_ipvs(net);
106 	spin_lock(&ipvs->est_lock);
107 	list_for_each_entry(e, &ipvs->est_list, list) {
108 		s = container_of(e, struct ip_vs_stats, est);
109 
110 		spin_lock(&s->lock);
111 		ip_vs_read_cpu_stats(&s->ustats, s->cpustats);
112 		n_conns = s->ustats.conns;
113 		n_inpkts = s->ustats.inpkts;
114 		n_outpkts = s->ustats.outpkts;
115 		n_inbytes = s->ustats.inbytes;
116 		n_outbytes = s->ustats.outbytes;
117 
118 		/* scaled by 2^10, but divided 2 seconds */
119 		rate = (n_conns - e->last_conns) << 9;
120 		e->last_conns = n_conns;
121 		e->cps += ((long)rate - (long)e->cps) >> 2;
122 
123 		rate = (n_inpkts - e->last_inpkts) << 9;
124 		e->last_inpkts = n_inpkts;
125 		e->inpps += ((long)rate - (long)e->inpps) >> 2;
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 
131 		rate = (n_inbytes - e->last_inbytes) << 4;
132 		e->last_inbytes = n_inbytes;
133 		e->inbps += ((long)rate - (long)e->inbps) >> 2;
134 
135 		rate = (n_outbytes - e->last_outbytes) << 4;
136 		e->last_outbytes = n_outbytes;
137 		e->outbps += ((long)rate - (long)e->outbps) >> 2;
138 		spin_unlock(&s->lock);
139 	}
140 	spin_unlock(&ipvs->est_lock);
141 	mod_timer(&ipvs->est_timer, jiffies + 2*HZ);
142 }
143 
144 void ip_vs_start_estimator(struct net *net, struct ip_vs_stats *stats)
145 {
146 	struct netns_ipvs *ipvs = net_ipvs(net);
147 	struct ip_vs_estimator *est = &stats->est;
148 
149 	INIT_LIST_HEAD(&est->list);
150 
151 	spin_lock_bh(&ipvs->est_lock);
152 	list_add(&est->list, &ipvs->est_list);
153 	spin_unlock_bh(&ipvs->est_lock);
154 }
155 
156 void ip_vs_stop_estimator(struct net *net, struct ip_vs_stats *stats)
157 {
158 	struct netns_ipvs *ipvs = net_ipvs(net);
159 	struct ip_vs_estimator *est = &stats->est;
160 
161 	spin_lock_bh(&ipvs->est_lock);
162 	list_del(&est->list);
163 	spin_unlock_bh(&ipvs->est_lock);
164 }
165 
166 void ip_vs_zero_estimator(struct ip_vs_stats *stats)
167 {
168 	struct ip_vs_estimator *est = &stats->est;
169 	struct ip_vs_stats_user *u = &stats->ustats;
170 
171 	/* reset counters, caller must hold the stats->lock lock */
172 	est->last_inbytes = u->inbytes;
173 	est->last_outbytes = u->outbytes;
174 	est->last_conns = u->conns;
175 	est->last_inpkts = u->inpkts;
176 	est->last_outpkts = u->outpkts;
177 	est->cps = 0;
178 	est->inpps = 0;
179 	est->outpps = 0;
180 	est->inbps = 0;
181 	est->outbps = 0;
182 }
183 
184 /* Get decoded rates */
185 void ip_vs_read_estimator(struct ip_vs_stats_user *dst,
186 			  struct ip_vs_stats *stats)
187 {
188 	struct ip_vs_estimator *e = &stats->est;
189 
190 	dst->cps = (e->cps + 0x1FF) >> 10;
191 	dst->inpps = (e->inpps + 0x1FF) >> 10;
192 	dst->outpps = (e->outpps + 0x1FF) >> 10;
193 	dst->inbps = (e->inbps + 0xF) >> 5;
194 	dst->outbps = (e->outbps + 0xF) >> 5;
195 }
196 
197 int __net_init ip_vs_estimator_net_init(struct net *net)
198 {
199 	struct netns_ipvs *ipvs = net_ipvs(net);
200 
201 	INIT_LIST_HEAD(&ipvs->est_list);
202 	spin_lock_init(&ipvs->est_lock);
203 	setup_timer(&ipvs->est_timer, estimation_timer, (unsigned long)net);
204 	mod_timer(&ipvs->est_timer, jiffies + 2 * HZ);
205 	return 0;
206 }
207 
208 void __net_exit ip_vs_estimator_net_cleanup(struct net *net)
209 {
210 	del_timer_sync(&net_ipvs(net)->est_timer);
211 }
212