]> Pileus Git - ~andy/linux/blob - net/netfilter/ipvs/ip_vs_est.c
ipvs: remove _bh from percpu stats reading
[~andy/linux] / net / netfilter / ipvs / ip_vs_est.c
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         ip_vs_read_cpu_stats(&ipvs->tot_stats->ustats, ipvs->cpustats);
105         spin_lock(&ipvs->est_lock);
106         list_for_each_entry(e, &ipvs->est_list, list) {
107                 s = container_of(e, struct ip_vs_stats, est);
108
109                 ip_vs_read_cpu_stats(&s->ustats, s->cpustats);
110                 spin_lock(&s->lock);
111                 n_conns = s->ustats.conns;
112                 n_inpkts = s->ustats.inpkts;
113                 n_outpkts = s->ustats.outpkts;
114                 n_inbytes = s->ustats.inbytes;
115                 n_outbytes = s->ustats.outbytes;
116
117                 /* scaled by 2^10, but divided 2 seconds */
118                 rate = (n_conns - e->last_conns) << 9;
119                 e->last_conns = n_conns;
120                 e->cps += ((long)rate - (long)e->cps) >> 2;
121                 s->ustats.cps = (e->cps + 0x1FF) >> 10;
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                 s->ustats.inpps = (e->inpps + 0x1FF) >> 10;
127
128                 rate = (n_outpkts - e->last_outpkts) << 9;
129                 e->last_outpkts = n_outpkts;
130                 e->outpps += ((long)rate - (long)e->outpps) >> 2;
131                 s->ustats.outpps = (e->outpps + 0x1FF) >> 10;
132
133                 rate = (n_inbytes - e->last_inbytes) << 4;
134                 e->last_inbytes = n_inbytes;
135                 e->inbps += ((long)rate - (long)e->inbps) >> 2;
136                 s->ustats.inbps = (e->inbps + 0xF) >> 5;
137
138                 rate = (n_outbytes - e->last_outbytes) << 4;
139                 e->last_outbytes = n_outbytes;
140                 e->outbps += ((long)rate - (long)e->outbps) >> 2;
141                 s->ustats.outbps = (e->outbps + 0xF) >> 5;
142                 spin_unlock(&s->lock);
143         }
144         spin_unlock(&ipvs->est_lock);
145         mod_timer(&ipvs->est_timer, jiffies + 2*HZ);
146 }
147
148 void ip_vs_new_estimator(struct net *net, struct ip_vs_stats *stats)
149 {
150         struct netns_ipvs *ipvs = net_ipvs(net);
151         struct ip_vs_estimator *est = &stats->est;
152
153         INIT_LIST_HEAD(&est->list);
154
155         est->last_conns = stats->ustats.conns;
156         est->cps = stats->ustats.cps<<10;
157
158         est->last_inpkts = stats->ustats.inpkts;
159         est->inpps = stats->ustats.inpps<<10;
160
161         est->last_outpkts = stats->ustats.outpkts;
162         est->outpps = stats->ustats.outpps<<10;
163
164         est->last_inbytes = stats->ustats.inbytes;
165         est->inbps = stats->ustats.inbps<<5;
166
167         est->last_outbytes = stats->ustats.outbytes;
168         est->outbps = stats->ustats.outbps<<5;
169
170         spin_lock_bh(&ipvs->est_lock);
171         list_add(&est->list, &ipvs->est_list);
172         spin_unlock_bh(&ipvs->est_lock);
173 }
174
175 void ip_vs_kill_estimator(struct net *net, struct ip_vs_stats *stats)
176 {
177         struct netns_ipvs *ipvs = net_ipvs(net);
178         struct ip_vs_estimator *est = &stats->est;
179
180         spin_lock_bh(&ipvs->est_lock);
181         list_del(&est->list);
182         spin_unlock_bh(&ipvs->est_lock);
183 }
184
185 void ip_vs_zero_estimator(struct ip_vs_stats *stats)
186 {
187         struct ip_vs_estimator *est = &stats->est;
188
189         /* set counters zero, caller must hold the stats->lock lock */
190         est->last_inbytes = 0;
191         est->last_outbytes = 0;
192         est->last_conns = 0;
193         est->last_inpkts = 0;
194         est->last_outpkts = 0;
195         est->cps = 0;
196         est->inpps = 0;
197         est->outpps = 0;
198         est->inbps = 0;
199         est->outbps = 0;
200 }
201
202 static int __net_init __ip_vs_estimator_init(struct net *net)
203 {
204         struct netns_ipvs *ipvs = net_ipvs(net);
205
206         INIT_LIST_HEAD(&ipvs->est_list);
207         spin_lock_init(&ipvs->est_lock);
208         setup_timer(&ipvs->est_timer, estimation_timer, (unsigned long)net);
209         mod_timer(&ipvs->est_timer, jiffies + 2 * HZ);
210         return 0;
211 }
212
213 static void __net_exit __ip_vs_estimator_exit(struct net *net)
214 {
215         del_timer_sync(&net_ipvs(net)->est_timer);
216 }
217 static struct pernet_operations ip_vs_app_ops = {
218         .init = __ip_vs_estimator_init,
219         .exit = __ip_vs_estimator_exit,
220 };
221
222 int __init ip_vs_estimator_init(void)
223 {
224         int rv;
225
226         rv = register_pernet_subsys(&ip_vs_app_ops);
227         return rv;
228 }
229
230 void ip_vs_estimator_cleanup(void)
231 {
232         unregister_pernet_subsys(&ip_vs_app_ops);
233 }