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 __percpu *stats) 60 { 61 int i; 62 bool add = false; 63 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; 68 if (add) { 69 sum->conns += s->ustats.conns; 70 sum->inpkts += s->ustats.inpkts; 71 sum->outpkts += s->ustats.outpkts; 72 do { 73 start = u64_stats_fetch_begin(&s->syncp); 74 inbytes = s->ustats.inbytes; 75 outbytes = s->ustats.outbytes; 76 } while (u64_stats_fetch_retry(&s->syncp, start)); 77 sum->inbytes += inbytes; 78 sum->outbytes += outbytes; 79 } else { 80 add = true; 81 sum->conns = s->ustats.conns; 82 sum->inpkts = s->ustats.inpkts; 83 sum->outpkts = s->ustats.outpkts; 84 do { 85 start = u64_stats_fetch_begin(&s->syncp); 86 sum->inbytes = s->ustats.inbytes; 87 sum->outbytes = s->ustats.outbytes; 88 } while (u64_stats_fetch_retry(&s->syncp, start)); 89 } 90 } 91 } 92 93 94 static 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; 102 struct net *net = (struct net *)arg; 103 struct netns_ipvs *ipvs; 104 105 ipvs = net_ipvs(net); 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->ustats, s->cpustats); 112 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; 117 118 /* scaled by 2^10, but divided 2 seconds */ 119 rate = (n_conns - e->last_conns) << 9; 120 e->last_conns = n_conns; 121 e->cps += ((long)rate - (long)e->cps) >> 2; 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 127 rate = (n_outpkts - e->last_outpkts) << 9; 128 e->last_outpkts = n_outpkts; 129 e->outpps += ((long)rate - (long)e->outpps) >> 2; 130 131 rate = (n_inbytes - e->last_inbytes) << 4; 132 e->last_inbytes = n_inbytes; 133 e->inbps += ((long)rate - (long)e->inbps) >> 2; 134 135 rate = (n_outbytes - e->last_outbytes) << 4; 136 e->last_outbytes = n_outbytes; 137 e->outbps += ((long)rate - (long)e->outbps) >> 2; 138 spin_unlock(&s->lock); 139 } 140 spin_unlock(&ipvs->est_lock); 141 mod_timer(&ipvs->est_timer, jiffies + 2*HZ); 142 } 143 144 void ip_vs_start_estimator(struct net *net, struct ip_vs_stats *stats) 145 { 146 struct netns_ipvs *ipvs = net_ipvs(net); 147 struct ip_vs_estimator *est = &stats->est; 148 149 INIT_LIST_HEAD(&est->list); 150 151 spin_lock_bh(&ipvs->est_lock); 152 list_add(&est->list, &ipvs->est_list); 153 spin_unlock_bh(&ipvs->est_lock); 154 } 155 156 void ip_vs_stop_estimator(struct net *net, struct ip_vs_stats *stats) 157 { 158 struct netns_ipvs *ipvs = net_ipvs(net); 159 struct ip_vs_estimator *est = &stats->est; 160 161 spin_lock_bh(&ipvs->est_lock); 162 list_del(&est->list); 163 spin_unlock_bh(&ipvs->est_lock); 164 } 165 166 void ip_vs_zero_estimator(struct ip_vs_stats *stats) 167 { 168 struct ip_vs_estimator *est = &stats->est; 169 struct ip_vs_stats_user *u = &stats->ustats; 170 171 /* 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; 177 est->cps = 0; 178 est->inpps = 0; 179 est->outpps = 0; 180 est->inbps = 0; 181 est->outbps = 0; 182 } 183 184 /* Get decoded rates */ 185 void 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 197 int __net_init ip_vs_estimator_net_init(struct net *net) 198 { 199 struct netns_ipvs *ipvs = net_ipvs(net); 200 201 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); 205 return 0; 206 } 207 208 void __net_exit ip_vs_estimator_net_cleanup(struct net *net) 209 { 210 del_timer_sync(&net_ipvs(net)->est_timer); 211 } 212