GNU Linux-libre 6.1.90-gnu
[releases.git] / net / netfilter / ipvs / ip_vs_est.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * ip_vs_est.c: simple rate estimator for IPVS
4  *
5  * Authors:     Wensong Zhang <wensong@linuxvirtualserver.org>
6  *
7  * Changes:     Hans Schillstrom <hans.schillstrom@ericsson.com>
8  *              Network name space (netns) aware.
9  *              Global data moved to netns i.e struct netns_ipvs
10  *              Affected data: est_list and est_lock.
11  *              estimation_timer() runs with timer per netns.
12  *              get_stats()) do the per cpu summing.
13  */
14
15 #define KMSG_COMPONENT "IPVS"
16 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
17
18 #include <linux/kernel.h>
19 #include <linux/jiffies.h>
20 #include <linux/types.h>
21 #include <linux/interrupt.h>
22 #include <linux/sysctl.h>
23 #include <linux/list.h>
24
25 #include <net/ip_vs.h>
26
27 /*
28   This code is to estimate rate in a shorter interval (such as 8
29   seconds) for virtual services and real servers. For measure rate in a
30   long interval, it is easy to implement a user level daemon which
31   periodically reads those statistical counters and measure rate.
32
33   Currently, the measurement is activated by slow timer handler. Hope
34   this measurement will not introduce too much load.
35
36   We measure rate during the last 8 seconds every 2 seconds:
37
38     avgrate = avgrate*(1-W) + rate*W
39
40     where W = 2^(-2)
41
42   NOTES.
43
44   * Average bps is scaled by 2^5, while average pps and cps are scaled by 2^10.
45
46   * Netlink users can see 64-bit values but sockopt users are restricted
47     to 32-bit values for conns, packets, bps, cps and pps.
48
49   * A lot of code is taken from net/core/gen_estimator.c
50  */
51
52
53 /*
54  * Make a summary from each cpu
55  */
56 static void ip_vs_read_cpu_stats(struct ip_vs_kstats *sum,
57                                  struct ip_vs_cpu_stats __percpu *stats)
58 {
59         int i;
60         bool add = false;
61
62         for_each_possible_cpu(i) {
63                 struct ip_vs_cpu_stats *s = per_cpu_ptr(stats, i);
64                 unsigned int start;
65                 u64 conns, inpkts, outpkts, inbytes, outbytes;
66
67                 if (add) {
68                         do {
69                                 start = u64_stats_fetch_begin(&s->syncp);
70                                 conns = u64_stats_read(&s->cnt.conns);
71                                 inpkts = u64_stats_read(&s->cnt.inpkts);
72                                 outpkts = u64_stats_read(&s->cnt.outpkts);
73                                 inbytes = u64_stats_read(&s->cnt.inbytes);
74                                 outbytes = u64_stats_read(&s->cnt.outbytes);
75                         } while (u64_stats_fetch_retry(&s->syncp, start));
76                         sum->conns += conns;
77                         sum->inpkts += inpkts;
78                         sum->outpkts += outpkts;
79                         sum->inbytes += inbytes;
80                         sum->outbytes += outbytes;
81                 } else {
82                         add = true;
83                         do {
84                                 start = u64_stats_fetch_begin(&s->syncp);
85                                 sum->conns = u64_stats_read(&s->cnt.conns);
86                                 sum->inpkts = u64_stats_read(&s->cnt.inpkts);
87                                 sum->outpkts = u64_stats_read(&s->cnt.outpkts);
88                                 sum->inbytes = u64_stats_read(&s->cnt.inbytes);
89                                 sum->outbytes = u64_stats_read(&s->cnt.outbytes);
90                         } while (u64_stats_fetch_retry(&s->syncp, start));
91                 }
92         }
93 }
94
95
96 static void estimation_timer(struct timer_list *t)
97 {
98         struct ip_vs_estimator *e;
99         struct ip_vs_stats *s;
100         u64 rate;
101         struct netns_ipvs *ipvs = from_timer(ipvs, t, est_timer);
102
103         if (!sysctl_run_estimation(ipvs))
104                 goto skip;
105
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->kstats, s->cpustats);
112
113                 /* scaled by 2^10, but divided 2 seconds */
114                 rate = (s->kstats.conns - e->last_conns) << 9;
115                 e->last_conns = s->kstats.conns;
116                 e->cps += ((s64)rate - (s64)e->cps) >> 2;
117
118                 rate = (s->kstats.inpkts - e->last_inpkts) << 9;
119                 e->last_inpkts = s->kstats.inpkts;
120                 e->inpps += ((s64)rate - (s64)e->inpps) >> 2;
121
122                 rate = (s->kstats.outpkts - e->last_outpkts) << 9;
123                 e->last_outpkts = s->kstats.outpkts;
124                 e->outpps += ((s64)rate - (s64)e->outpps) >> 2;
125
126                 /* scaled by 2^5, but divided 2 seconds */
127                 rate = (s->kstats.inbytes - e->last_inbytes) << 4;
128                 e->last_inbytes = s->kstats.inbytes;
129                 e->inbps += ((s64)rate - (s64)e->inbps) >> 2;
130
131                 rate = (s->kstats.outbytes - e->last_outbytes) << 4;
132                 e->last_outbytes = s->kstats.outbytes;
133                 e->outbps += ((s64)rate - (s64)e->outbps) >> 2;
134                 spin_unlock(&s->lock);
135         }
136         spin_unlock(&ipvs->est_lock);
137
138 skip:
139         mod_timer(&ipvs->est_timer, jiffies + 2*HZ);
140 }
141
142 void ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats)
143 {
144         struct ip_vs_estimator *est = &stats->est;
145
146         INIT_LIST_HEAD(&est->list);
147
148         spin_lock_bh(&ipvs->est_lock);
149         list_add(&est->list, &ipvs->est_list);
150         spin_unlock_bh(&ipvs->est_lock);
151 }
152
153 void ip_vs_stop_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats)
154 {
155         struct ip_vs_estimator *est = &stats->est;
156
157         spin_lock_bh(&ipvs->est_lock);
158         list_del(&est->list);
159         spin_unlock_bh(&ipvs->est_lock);
160 }
161
162 void ip_vs_zero_estimator(struct ip_vs_stats *stats)
163 {
164         struct ip_vs_estimator *est = &stats->est;
165         struct ip_vs_kstats *k = &stats->kstats;
166
167         /* reset counters, caller must hold the stats->lock lock */
168         est->last_inbytes = k->inbytes;
169         est->last_outbytes = k->outbytes;
170         est->last_conns = k->conns;
171         est->last_inpkts = k->inpkts;
172         est->last_outpkts = k->outpkts;
173         est->cps = 0;
174         est->inpps = 0;
175         est->outpps = 0;
176         est->inbps = 0;
177         est->outbps = 0;
178 }
179
180 /* Get decoded rates */
181 void ip_vs_read_estimator(struct ip_vs_kstats *dst, struct ip_vs_stats *stats)
182 {
183         struct ip_vs_estimator *e = &stats->est;
184
185         dst->cps = (e->cps + 0x1FF) >> 10;
186         dst->inpps = (e->inpps + 0x1FF) >> 10;
187         dst->outpps = (e->outpps + 0x1FF) >> 10;
188         dst->inbps = (e->inbps + 0xF) >> 5;
189         dst->outbps = (e->outbps + 0xF) >> 5;
190 }
191
192 int __net_init ip_vs_estimator_net_init(struct netns_ipvs *ipvs)
193 {
194         INIT_LIST_HEAD(&ipvs->est_list);
195         spin_lock_init(&ipvs->est_lock);
196         timer_setup(&ipvs->est_timer, estimation_timer, 0);
197         mod_timer(&ipvs->est_timer, jiffies + 2 * HZ);
198         return 0;
199 }
200
201 void __net_exit ip_vs_estimator_net_cleanup(struct netns_ipvs *ipvs)
202 {
203         del_timer_sync(&ipvs->est_timer);
204 }