blob: 07d839bef53757d6bcec96cdc440e3ff1ca4406c [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070055static void estimation_timer(unsigned long arg)
56{
57 struct ip_vs_estimator *e;
58 struct ip_vs_stats *s;
59 u32 n_conns;
60 u32 n_inpkts, n_outpkts;
61 u64 n_inbytes, n_outbytes;
62 u32 rate;
Hans Schillstrom29c20262011-01-03 14:44:54 +010063 struct net *net = (struct net *)arg;
64 struct netns_ipvs *ipvs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
Hans Schillstrom29c20262011-01-03 14:44:54 +010066 ipvs = net_ipvs(net);
67 spin_lock(&ipvs->est_lock);
68 list_for_each_entry(e, &ipvs->est_list, list) {
Sven Wegener3a14a3132008-08-10 18:24:41 +000069 s = container_of(e, struct ip_vs_stats, est);
Linus Torvalds1da177e2005-04-16 15:20:36 -070070
71 spin_lock(&s->lock);
Sven Wegenere9c0ce22008-09-08 13:39:04 +020072 n_conns = s->ustats.conns;
73 n_inpkts = s->ustats.inpkts;
74 n_outpkts = s->ustats.outpkts;
75 n_inbytes = s->ustats.inbytes;
76 n_outbytes = s->ustats.outbytes;
Linus Torvalds1da177e2005-04-16 15:20:36 -070077
78 /* scaled by 2^10, but divided 2 seconds */
Hans Schillstrom29c20262011-01-03 14:44:54 +010079 rate = (n_conns - e->last_conns) << 9;
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 e->last_conns = n_conns;
Hans Schillstrom29c20262011-01-03 14:44:54 +010081 e->cps += ((long)rate - (long)e->cps) >> 2;
82 s->ustats.cps = (e->cps + 0x1FF) >> 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
Hans Schillstrom29c20262011-01-03 14:44:54 +010084 rate = (n_inpkts - e->last_inpkts) << 9;
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 e->last_inpkts = n_inpkts;
Hans Schillstrom29c20262011-01-03 14:44:54 +010086 e->inpps += ((long)rate - (long)e->inpps) >> 2;
87 s->ustats.inpps = (e->inpps + 0x1FF) >> 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -070088
Hans Schillstrom29c20262011-01-03 14:44:54 +010089 rate = (n_outpkts - e->last_outpkts) << 9;
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 e->last_outpkts = n_outpkts;
Hans Schillstrom29c20262011-01-03 14:44:54 +010091 e->outpps += ((long)rate - (long)e->outpps) >> 2;
92 s->ustats.outpps = (e->outpps + 0x1FF) >> 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -070093
Hans Schillstrom29c20262011-01-03 14:44:54 +010094 rate = (n_inbytes - e->last_inbytes) << 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 e->last_inbytes = n_inbytes;
Hans Schillstrom29c20262011-01-03 14:44:54 +010096 e->inbps += ((long)rate - (long)e->inbps) >> 2;
97 s->ustats.inbps = (e->inbps + 0xF) >> 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -070098
Hans Schillstrom29c20262011-01-03 14:44:54 +010099 rate = (n_outbytes - e->last_outbytes) << 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 e->last_outbytes = n_outbytes;
Hans Schillstrom29c20262011-01-03 14:44:54 +0100101 e->outbps += ((long)rate - (long)e->outbps) >> 2;
102 s->ustats.outbps = (e->outbps + 0xF) >> 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 spin_unlock(&s->lock);
104 }
Hans Schillstrom29c20262011-01-03 14:44:54 +0100105 spin_unlock(&ipvs->est_lock);
106 mod_timer(&ipvs->est_timer, jiffies + 2*HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107}
108
Hans Schillstrom29c20262011-01-03 14:44:54 +0100109void ip_vs_new_estimator(struct net *net, struct ip_vs_stats *stats)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110{
Hans Schillstrom29c20262011-01-03 14:44:54 +0100111 struct netns_ipvs *ipvs = net_ipvs(net);
Sven Wegener3a14a3132008-08-10 18:24:41 +0000112 struct ip_vs_estimator *est = &stats->est;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113
Sven Wegener3a14a3132008-08-10 18:24:41 +0000114 INIT_LIST_HEAD(&est->list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115
Sven Wegenere9c0ce22008-09-08 13:39:04 +0200116 est->last_conns = stats->ustats.conns;
117 est->cps = stats->ustats.cps<<10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118
Sven Wegenere9c0ce22008-09-08 13:39:04 +0200119 est->last_inpkts = stats->ustats.inpkts;
120 est->inpps = stats->ustats.inpps<<10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121
Sven Wegenere9c0ce22008-09-08 13:39:04 +0200122 est->last_outpkts = stats->ustats.outpkts;
123 est->outpps = stats->ustats.outpps<<10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124
Sven Wegenere9c0ce22008-09-08 13:39:04 +0200125 est->last_inbytes = stats->ustats.inbytes;
126 est->inbps = stats->ustats.inbps<<5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127
Sven Wegenere9c0ce22008-09-08 13:39:04 +0200128 est->last_outbytes = stats->ustats.outbytes;
129 est->outbps = stats->ustats.outbps<<5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130
Hans Schillstrom29c20262011-01-03 14:44:54 +0100131 spin_lock_bh(&ipvs->est_lock);
132 list_add(&est->list, &ipvs->est_list);
133 spin_unlock_bh(&ipvs->est_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134}
135
Hans Schillstrom29c20262011-01-03 14:44:54 +0100136void ip_vs_kill_estimator(struct net *net, struct ip_vs_stats *stats)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137{
Hans Schillstrom29c20262011-01-03 14:44:54 +0100138 struct netns_ipvs *ipvs = net_ipvs(net);
Sven Wegener3a14a3132008-08-10 18:24:41 +0000139 struct ip_vs_estimator *est = &stats->est;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140
Hans Schillstrom29c20262011-01-03 14:44:54 +0100141 spin_lock_bh(&ipvs->est_lock);
Sven Wegener3a14a3132008-08-10 18:24:41 +0000142 list_del(&est->list);
Hans Schillstrom29c20262011-01-03 14:44:54 +0100143 spin_unlock_bh(&ipvs->est_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144}
145
146void ip_vs_zero_estimator(struct ip_vs_stats *stats)
147{
Sven Wegener3a14a3132008-08-10 18:24:41 +0000148 struct ip_vs_estimator *est = &stats->est;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149
Sven Wegener3a14a3132008-08-10 18:24:41 +0000150 /* set counters zero, caller must hold the stats->lock lock */
151 est->last_inbytes = 0;
152 est->last_outbytes = 0;
153 est->last_conns = 0;
154 est->last_inpkts = 0;
155 est->last_outpkts = 0;
156 est->cps = 0;
157 est->inpps = 0;
158 est->outpps = 0;
159 est->inbps = 0;
160 est->outbps = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161}
Sven Wegenera919cf42008-08-14 00:47:16 +0200162
Hans Schillstrom61b1ab42011-01-03 14:44:42 +0100163static int __net_init __ip_vs_estimator_init(struct net *net)
164{
Hans Schillstrom29c20262011-01-03 14:44:54 +0100165 struct netns_ipvs *ipvs = net_ipvs(net);
166
Hans Schillstrom61b1ab42011-01-03 14:44:42 +0100167 if (!net_eq(net, &init_net)) /* netns not enabled yet */
168 return -EPERM;
169
Hans Schillstrom29c20262011-01-03 14:44:54 +0100170 INIT_LIST_HEAD(&ipvs->est_list);
171 spin_lock_init(&ipvs->est_lock);
172 setup_timer(&ipvs->est_timer, estimation_timer, (unsigned long)net);
173 mod_timer(&ipvs->est_timer, jiffies + 2 * HZ);
Hans Schillstrom61b1ab42011-01-03 14:44:42 +0100174 return 0;
175}
176
Hans Schillstrom29c20262011-01-03 14:44:54 +0100177static void __net_exit __ip_vs_estimator_exit(struct net *net)
178{
179 del_timer_sync(&net_ipvs(net)->est_timer);
180}
Hans Schillstrom61b1ab42011-01-03 14:44:42 +0100181static struct pernet_operations ip_vs_app_ops = {
182 .init = __ip_vs_estimator_init,
Hans Schillstrom29c20262011-01-03 14:44:54 +0100183 .exit = __ip_vs_estimator_exit,
Hans Schillstrom61b1ab42011-01-03 14:44:42 +0100184};
185
Sven Wegenera919cf42008-08-14 00:47:16 +0200186int __init ip_vs_estimator_init(void)
187{
Hans Schillstrom61b1ab42011-01-03 14:44:42 +0100188 int rv;
189
190 rv = register_pernet_subsys(&ip_vs_app_ops);
Hans Schillstrom61b1ab42011-01-03 14:44:42 +0100191 return rv;
Sven Wegenera919cf42008-08-14 00:47:16 +0200192}
193
194void ip_vs_estimator_cleanup(void)
195{
Hans Schillstrom61b1ab42011-01-03 14:44:42 +0100196 unregister_pernet_subsys(&ip_vs_app_ops);
Sven Wegenera919cf42008-08-14 00:47:16 +0200197}