blob: 1425e9a924c4f64429637bc49cbde204b0bb1921 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * ip_vs_est.c: simple rate estimator for IPVS
3 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * 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 *
Hans Schillstrom29c20262011-01-03 14:44:54 +010011 * 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.
Linus Torvalds1da177e2005-04-16 15:20:36 -070017 */
Hannes Eder9aada7a2009-07-30 14:29:44 -070018
19#define KMSG_COMPONENT "IPVS"
20#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
21
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/kernel.h>
Arnaldo Carvalho de Melo14c85022005-12-27 02:43:12 -020023#include <linux/jiffies.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include <linux/types.h>
Adrian Bunk4ffd2e42006-01-05 12:14:43 -080025#include <linux/interrupt.h>
Pavel Emelyanov90754f82008-01-12 02:33:50 -080026#include <linux/sysctl.h>
Sven Wegener3a14a3132008-08-10 18:24:41 +000027#include <linux/list.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
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
Hans Schillstromb17fc992011-01-03 14:44:56 +010055/*
56 * Make a summary from each cpu
57 */
58static void ip_vs_read_cpu_stats(struct ip_vs_stats_user *sum,
Julian Anastasovb962abd2013-03-09 23:25:08 +020059 struct ip_vs_cpu_stats __percpu *stats)
Hans Schillstromb17fc992011-01-03 14:44:56 +010060{
61 int i;
Julian Anastasovd1ee4fe2013-09-12 11:21:10 +030062 bool add = false;
Hans Schillstromb17fc992011-01-03 14:44:56 +010063
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;
Julian Anastasovd1ee4fe2013-09-12 11:21:10 +030068 if (add) {
Hans Schillstromb17fc992011-01-03 14:44:56 +010069 sum->conns += s->ustats.conns;
70 sum->inpkts += s->ustats.inpkts;
71 sum->outpkts += s->ustats.outpkts;
72 do {
Julian Anastasov4a569c02011-03-04 12:28:20 +020073 start = u64_stats_fetch_begin(&s->syncp);
Hans Schillstromb17fc992011-01-03 14:44:56 +010074 inbytes = s->ustats.inbytes;
75 outbytes = s->ustats.outbytes;
Julian Anastasov4a569c02011-03-04 12:28:20 +020076 } while (u64_stats_fetch_retry(&s->syncp, start));
Hans Schillstromb17fc992011-01-03 14:44:56 +010077 sum->inbytes += inbytes;
78 sum->outbytes += outbytes;
79 } else {
Julian Anastasovd1ee4fe2013-09-12 11:21:10 +030080 add = true;
Hans Schillstromb17fc992011-01-03 14:44:56 +010081 sum->conns = s->ustats.conns;
82 sum->inpkts = s->ustats.inpkts;
83 sum->outpkts = s->ustats.outpkts;
84 do {
Julian Anastasov4a569c02011-03-04 12:28:20 +020085 start = u64_stats_fetch_begin(&s->syncp);
Hans Schillstromb17fc992011-01-03 14:44:56 +010086 sum->inbytes = s->ustats.inbytes;
87 sum->outbytes = s->ustats.outbytes;
Julian Anastasov4a569c02011-03-04 12:28:20 +020088 } while (u64_stats_fetch_retry(&s->syncp, start));
Hans Schillstromb17fc992011-01-03 14:44:56 +010089 }
90 }
91}
92
93
Linus Torvalds1da177e2005-04-16 15:20:36 -070094static 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;
Hans Schillstrom29c20262011-01-03 14:44:54 +0100102 struct net *net = (struct net *)arg;
103 struct netns_ipvs *ipvs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104
Hans Schillstrom29c20262011-01-03 14:44:54 +0100105 ipvs = net_ipvs(net);
106 spin_lock(&ipvs->est_lock);
107 list_for_each_entry(e, &ipvs->est_list, list) {
Sven Wegener3a14a3132008-08-10 18:24:41 +0000108 s = container_of(e, struct ip_vs_stats, est);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109
110 spin_lock(&s->lock);
Julian Anastasov2a0751a2011-03-04 12:20:35 +0200111 ip_vs_read_cpu_stats(&s->ustats, s->cpustats);
Sven Wegenere9c0ce22008-09-08 13:39:04 +0200112 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
118 /* scaled by 2^10, but divided 2 seconds */
Hans Schillstrom29c20262011-01-03 14:44:54 +0100119 rate = (n_conns - e->last_conns) << 9;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 e->last_conns = n_conns;
Hans Schillstrom29c20262011-01-03 14:44:54 +0100121 e->cps += ((long)rate - (long)e->cps) >> 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122
Hans Schillstrom29c20262011-01-03 14:44:54 +0100123 rate = (n_inpkts - e->last_inpkts) << 9;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 e->last_inpkts = n_inpkts;
Hans Schillstrom29c20262011-01-03 14:44:54 +0100125 e->inpps += ((long)rate - (long)e->inpps) >> 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
Hans Schillstrom29c20262011-01-03 14:44:54 +0100127 rate = (n_outpkts - e->last_outpkts) << 9;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 e->last_outpkts = n_outpkts;
Hans Schillstrom29c20262011-01-03 14:44:54 +0100129 e->outpps += ((long)rate - (long)e->outpps) >> 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130
Hans Schillstrom29c20262011-01-03 14:44:54 +0100131 rate = (n_inbytes - e->last_inbytes) << 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 e->last_inbytes = n_inbytes;
Hans Schillstrom29c20262011-01-03 14:44:54 +0100133 e->inbps += ((long)rate - (long)e->inbps) >> 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134
Hans Schillstrom29c20262011-01-03 14:44:54 +0100135 rate = (n_outbytes - e->last_outbytes) << 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 e->last_outbytes = n_outbytes;
Hans Schillstrom29c20262011-01-03 14:44:54 +0100137 e->outbps += ((long)rate - (long)e->outbps) >> 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 spin_unlock(&s->lock);
139 }
Hans Schillstrom29c20262011-01-03 14:44:54 +0100140 spin_unlock(&ipvs->est_lock);
141 mod_timer(&ipvs->est_timer, jiffies + 2*HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142}
143
Julian Anastasov6ef757f2011-03-14 01:44:28 +0200144void ip_vs_start_estimator(struct net *net, struct ip_vs_stats *stats)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145{
Hans Schillstrom29c20262011-01-03 14:44:54 +0100146 struct netns_ipvs *ipvs = net_ipvs(net);
Sven Wegener3a14a3132008-08-10 18:24:41 +0000147 struct ip_vs_estimator *est = &stats->est;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148
Sven Wegener3a14a3132008-08-10 18:24:41 +0000149 INIT_LIST_HEAD(&est->list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150
Hans Schillstrom29c20262011-01-03 14:44:54 +0100151 spin_lock_bh(&ipvs->est_lock);
152 list_add(&est->list, &ipvs->est_list);
153 spin_unlock_bh(&ipvs->est_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154}
155
Julian Anastasov6ef757f2011-03-14 01:44:28 +0200156void ip_vs_stop_estimator(struct net *net, struct ip_vs_stats *stats)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157{
Hans Schillstrom29c20262011-01-03 14:44:54 +0100158 struct netns_ipvs *ipvs = net_ipvs(net);
Sven Wegener3a14a3132008-08-10 18:24:41 +0000159 struct ip_vs_estimator *est = &stats->est;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160
Hans Schillstrom29c20262011-01-03 14:44:54 +0100161 spin_lock_bh(&ipvs->est_lock);
Sven Wegener3a14a3132008-08-10 18:24:41 +0000162 list_del(&est->list);
Hans Schillstrom29c20262011-01-03 14:44:54 +0100163 spin_unlock_bh(&ipvs->est_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164}
165
166void ip_vs_zero_estimator(struct ip_vs_stats *stats)
167{
Sven Wegener3a14a3132008-08-10 18:24:41 +0000168 struct ip_vs_estimator *est = &stats->est;
Julian Anastasov55a3d4e2011-03-14 01:37:49 +0200169 struct ip_vs_stats_user *u = &stats->ustats;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170
Julian Anastasov55a3d4e2011-03-14 01:37:49 +0200171 /* 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;
Sven Wegener3a14a3132008-08-10 18:24:41 +0000177 est->cps = 0;
178 est->inpps = 0;
179 est->outpps = 0;
180 est->inbps = 0;
181 est->outbps = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182}
Sven Wegenera919cf42008-08-14 00:47:16 +0200183
Julian Anastasovea9f22c2011-03-14 01:41:54 +0200184/* Get decoded rates */
185void 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
Hans Schillstrom503cf152011-05-01 18:50:16 +0200197int __net_init ip_vs_estimator_net_init(struct net *net)
Hans Schillstrom61b1ab42011-01-03 14:44:42 +0100198{
Hans Schillstrom29c20262011-01-03 14:44:54 +0100199 struct netns_ipvs *ipvs = net_ipvs(net);
200
Hans Schillstrom29c20262011-01-03 14:44:54 +0100201 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);
Hans Schillstrom61b1ab42011-01-03 14:44:42 +0100205 return 0;
206}
207
Hans Schillstrom503cf152011-05-01 18:50:16 +0200208void __net_exit ip_vs_estimator_net_cleanup(struct net *net)
Hans Schillstrom29c20262011-01-03 14:44:54 +0100209{
210 del_timer_sync(&net_ipvs(net)->est_timer);
211}