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        if (!sysctl_run_estimation(ipvs))
 104                goto skip;
 105
 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->kstats, s->cpustats);
 112
 113                /* scaled by 2^10, but divided 2 seconds */
 114                rate = (s->kstats.conns - e->last_conns) << 9;
 115                e->last_conns = s->kstats.conns;
 116                e->cps += ((s64)rate - (s64)e->cps) >> 2;
 117
 118                rate = (s->kstats.inpkts - e->last_inpkts) << 9;
 119                e->last_inpkts = s->kstats.inpkts;
 120                e->inpps += ((s64)rate - (s64)e->inpps) >> 2;
 121
 122                rate = (s->kstats.outpkts - e->last_outpkts) << 9;
 123                e->last_outpkts = s->kstats.outpkts;
 124                e->outpps += ((s64)rate - (s64)e->outpps) >> 2;
 125
 126                /* scaled by 2^5, but divided 2 seconds */
 127                rate = (s->kstats.inbytes - e->last_inbytes) << 4;
 128                e->last_inbytes = s->kstats.inbytes;
 129                e->inbps += ((s64)rate - (s64)e->inbps) >> 2;
 130
 131                rate = (s->kstats.outbytes - e->last_outbytes) << 4;
 132                e->last_outbytes = s->kstats.outbytes;
 133                e->outbps += ((s64)rate - (s64)e->outbps) >> 2;
 134                spin_unlock(&s->lock);
 135        }
 136        spin_unlock(&ipvs->est_lock);
 137
 138skip:
 139        mod_timer(&ipvs->est_timer, jiffies + 2*HZ);
 140}
 141
 142void ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats)
 143{
 144        struct ip_vs_estimator *est = &stats->est;
 145
 146        INIT_LIST_HEAD(&est->list);
 147
 148        spin_lock_bh(&ipvs->est_lock);
 149        list_add(&est->list, &ipvs->est_list);
 150        spin_unlock_bh(&ipvs->est_lock);
 151}
 152
 153void ip_vs_stop_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats)
 154{
 155        struct ip_vs_estimator *est = &stats->est;
 156
 157        spin_lock_bh(&ipvs->est_lock);
 158        list_del(&est->list);
 159        spin_unlock_bh(&ipvs->est_lock);
 160}
 161
 162void ip_vs_zero_estimator(struct ip_vs_stats *stats)
 163{
 164        struct ip_vs_estimator *est = &stats->est;
 165        struct ip_vs_kstats *k = &stats->kstats;
 166
 167        /* reset counters, caller must hold the stats->lock lock */
 168        est->last_inbytes = k->inbytes;
 169        est->last_outbytes = k->outbytes;
 170        est->last_conns = k->conns;
 171        est->last_inpkts = k->inpkts;
 172        est->last_outpkts = k->outpkts;
 173        est->cps = 0;
 174        est->inpps = 0;
 175        est->outpps = 0;
 176        est->inbps = 0;
 177        est->outbps = 0;
 178}
 179
 180/* Get decoded rates */
 181void ip_vs_read_estimator(struct ip_vs_kstats *dst, struct ip_vs_stats *stats)
 182{
 183        struct ip_vs_estimator *e = &stats->est;
 184
 185        dst->cps = (e->cps + 0x1FF) >> 10;
 186        dst->inpps = (e->inpps + 0x1FF) >> 10;
 187        dst->outpps = (e->outpps + 0x1FF) >> 10;
 188        dst->inbps = (e->inbps + 0xF) >> 5;
 189        dst->outbps = (e->outbps + 0xF) >> 5;
 190}
 191
 192int __net_init ip_vs_estimator_net_init(struct netns_ipvs *ipvs)
 193{
 194        INIT_LIST_HEAD(&ipvs->est_list);
 195        spin_lock_init(&ipvs->est_lock);
 196        timer_setup(&ipvs->est_timer, estimation_timer, 0);
 197        mod_timer(&ipvs->est_timer, jiffies + 2 * HZ);
 198        return 0;
 199}
 200
 201void __net_exit ip_vs_estimator_net_cleanup(struct netns_ipvs *ipvs)
 202{
 203        del_timer_sync(&ipvs->est_timer);
 204}
 205