xref: /openbmc/linux/net/netfilter/ipvs/ip_vs_est.c (revision 6c8f7949)
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 
121 		rate = (n_inpkts - e->last_inpkts) << 9;
122 		e->last_inpkts = n_inpkts;
123 		e->inpps += ((long)rate - (long)e->inpps) >> 2;
124 
125 		rate = (n_outpkts - e->last_outpkts) << 9;
126 		e->last_outpkts = n_outpkts;
127 		e->outpps += ((long)rate - (long)e->outpps) >> 2;
128 
129 		rate = (n_inbytes - e->last_inbytes) << 4;
130 		e->last_inbytes = n_inbytes;
131 		e->inbps += ((long)rate - (long)e->inbps) >> 2;
132 
133 		rate = (n_outbytes - e->last_outbytes) << 4;
134 		e->last_outbytes = n_outbytes;
135 		e->outbps += ((long)rate - (long)e->outbps) >> 2;
136 		spin_unlock(&s->lock);
137 	}
138 	spin_unlock(&ipvs->est_lock);
139 	mod_timer(&ipvs->est_timer, jiffies + 2*HZ);
140 }
141 
142 void ip_vs_start_estimator(struct net *net, struct ip_vs_stats *stats)
143 {
144 	struct netns_ipvs *ipvs = net_ipvs(net);
145 	struct ip_vs_estimator *est = &stats->est;
146 
147 	INIT_LIST_HEAD(&est->list);
148 
149 	spin_lock_bh(&ipvs->est_lock);
150 	list_add(&est->list, &ipvs->est_list);
151 	spin_unlock_bh(&ipvs->est_lock);
152 }
153 
154 void ip_vs_stop_estimator(struct net *net, struct ip_vs_stats *stats)
155 {
156 	struct netns_ipvs *ipvs = net_ipvs(net);
157 	struct ip_vs_estimator *est = &stats->est;
158 
159 	spin_lock_bh(&ipvs->est_lock);
160 	list_del(&est->list);
161 	spin_unlock_bh(&ipvs->est_lock);
162 }
163 
164 void ip_vs_zero_estimator(struct ip_vs_stats *stats)
165 {
166 	struct ip_vs_estimator *est = &stats->est;
167 	struct ip_vs_stats_user *u = &stats->ustats;
168 
169 	/* reset counters, caller must hold the stats->lock lock */
170 	est->last_inbytes = u->inbytes;
171 	est->last_outbytes = u->outbytes;
172 	est->last_conns = u->conns;
173 	est->last_inpkts = u->inpkts;
174 	est->last_outpkts = u->outpkts;
175 	est->cps = 0;
176 	est->inpps = 0;
177 	est->outpps = 0;
178 	est->inbps = 0;
179 	est->outbps = 0;
180 }
181 
182 /* Get decoded rates */
183 void ip_vs_read_estimator(struct ip_vs_stats_user *dst,
184 			  struct ip_vs_stats *stats)
185 {
186 	struct ip_vs_estimator *e = &stats->est;
187 
188 	dst->cps = (e->cps + 0x1FF) >> 10;
189 	dst->inpps = (e->inpps + 0x1FF) >> 10;
190 	dst->outpps = (e->outpps + 0x1FF) >> 10;
191 	dst->inbps = (e->inbps + 0xF) >> 5;
192 	dst->outbps = (e->outbps + 0xF) >> 5;
193 }
194 
195 int __net_init ip_vs_estimator_net_init(struct net *net)
196 {
197 	struct netns_ipvs *ipvs = net_ipvs(net);
198 
199 	INIT_LIST_HEAD(&ipvs->est_list);
200 	spin_lock_init(&ipvs->est_lock);
201 	setup_timer(&ipvs->est_timer, estimation_timer, (unsigned long)net);
202 	mod_timer(&ipvs->est_timer, jiffies + 2 * HZ);
203 	return 0;
204 }
205 
206 void __net_exit ip_vs_estimator_net_cleanup(struct net *net)
207 {
208 	del_timer_sync(&net_ipvs(net)->est_timer);
209 }
210