linux/net/netfilter/ipvs/ip_vs_est.c
<<
>>
Prefs
   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 */
  56static 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 = s->cnt.conns;
  71                                inpkts = s->cnt.inpkts;
  72                                outpkts = s->cnt.outpkts;
  73                                inbytes = s->cnt.inbytes;
  74                                outbytes = 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 = s->cnt.conns;
  86                                sum->inpkts = s->cnt.inpkts;
  87                                sum->outpkts = s->cnt.outpkts;
  88                                sum->inbytes = s->cnt.inbytes;
  89                                sum->outbytes = s->cnt.outbytes;
  90                        } while (u64_stats_fetch_retry(&s->syncp, start));
  91                }
  92        }
  93}
  94
  95
  96static 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        spin_lock(&ipvs->est_lock);
 104        list_for_each_entry(e, &ipvs->est_list, list) {
 105                s = container_of(e, struct ip_vs_stats, est);
 106
 107                spin_lock(&s->lock);
 108                ip_vs_read_cpu_stats(&s->kstats, s->cpustats);
 109
 110                /* scaled by 2^10, but divided 2 seconds */
 111                rate = (s->kstats.conns - e->last_conns) << 9;
 112                e->last_conns = s->kstats.conns;
 113                e->cps += ((s64)rate - (s64)e->cps) >> 2;
 114
 115                rate = (s->kstats.inpkts - e->last_inpkts) << 9;
 116                e->last_inpkts = s->kstats.inpkts;
 117                e->inpps += ((s64)rate - (s64)e->inpps) >> 2;
 118
 119                rate = (s->kstats.outpkts - e->last_outpkts) << 9;
 120                e->last_outpkts = s->kstats.outpkts;
 121                e->outpps += ((s64)rate - (s64)e->outpps) >> 2;
 122
 123                /* scaled by 2^5, but divided 2 seconds */
 124                rate = (s->kstats.inbytes - e->last_inbytes) << 4;
 125                e->last_inbytes = s->kstats.inbytes;
 126                e->inbps += ((s64)rate - (s64)e->inbps) >> 2;
 127
 128                rate = (s->kstats.outbytes - e->last_outbytes) << 4;
 129                e->last_outbytes = s->kstats.outbytes;
 130                e->outbps += ((s64)rate - (s64)e->outbps) >> 2;
 131                spin_unlock(&s->lock);
 132        }
 133        spin_unlock(&ipvs->est_lock);
 134        mod_timer(&ipvs->est_timer, jiffies + 2*HZ);
 135}
 136
 137void ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats)
 138{
 139        struct ip_vs_estimator *est = &stats->est;
 140
 141        INIT_LIST_HEAD(&est->list);
 142
 143        spin_lock_bh(&ipvs->est_lock);
 144        list_add(&est->list, &ipvs->est_list);
 145        spin_unlock_bh(&ipvs->est_lock);
 146}
 147
 148void ip_vs_stop_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats)
 149{
 150        struct ip_vs_estimator *est = &stats->est;
 151
 152        spin_lock_bh(&ipvs->est_lock);
 153        list_del(&est->list);
 154        spin_unlock_bh(&ipvs->est_lock);
 155}
 156
 157void ip_vs_zero_estimator(struct ip_vs_stats *stats)
 158{
 159        struct ip_vs_estimator *est = &stats->est;
 160        struct ip_vs_kstats *k = &stats->kstats;
 161
 162        /* reset counters, caller must hold the stats->lock lock */
 163        est->last_inbytes = k->inbytes;
 164        est->last_outbytes = k->outbytes;
 165        est->last_conns = k->conns;
 166        est->last_inpkts = k->inpkts;
 167        est->last_outpkts = k->outpkts;
 168        est->cps = 0;
 169        est->inpps = 0;
 170        est->outpps = 0;
 171        est->inbps = 0;
 172        est->outbps = 0;
 173}
 174
 175/* Get decoded rates */
 176void ip_vs_read_estimator(struct ip_vs_kstats *dst, struct ip_vs_stats *stats)
 177{
 178        struct ip_vs_estimator *e = &stats->est;
 179
 180        dst->cps = (e->cps + 0x1FF) >> 10;
 181        dst->inpps = (e->inpps + 0x1FF) >> 10;
 182        dst->outpps = (e->outpps + 0x1FF) >> 10;
 183        dst->inbps = (e->inbps + 0xF) >> 5;
 184        dst->outbps = (e->outbps + 0xF) >> 5;
 185}
 186
 187int __net_init ip_vs_estimator_net_init(struct netns_ipvs *ipvs)
 188{
 189        INIT_LIST_HEAD(&ipvs->est_list);
 190        spin_lock_init(&ipvs->est_lock);
 191        timer_setup(&ipvs->est_timer, estimation_timer, 0);
 192        mod_timer(&ipvs->est_timer, jiffies + 2 * HZ);
 193        return 0;
 194}
 195
 196void __net_exit ip_vs_estimator_net_cleanup(struct netns_ipvs *ipvs)
 197{
 198        del_timer_sync(&ipvs->est_timer);
 199}
 200