Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ip_vs_est.c
Go to the documentation of this file.
1 /*
2  * ip_vs_est.c: simple rate estimator for IPVS
3  *
4  * Authors: Wensong Zhang <[email protected]>
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 <[email protected]>
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 
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 
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 */
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 
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 
207 {
208  del_timer_sync(&net_ipvs(net)->est_timer);
209 }