LCOV - code coverage report
Current view: top level - net/core - gen_stats.c (source / functions) Hit Total Coverage
Test: landlock.info Lines: 0 143 0.0 %
Date: 2021-04-22 12:43:58 Functions: 0 14 0.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-or-later
       2             : /*
       3             :  * net/core/gen_stats.c
       4             :  *
       5             :  * Authors:  Thomas Graf <tgraf@suug.ch>
       6             :  *           Jamal Hadi Salim
       7             :  *           Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
       8             :  *
       9             :  * See Documentation/networking/gen_stats.rst
      10             :  */
      11             : 
      12             : #include <linux/types.h>
      13             : #include <linux/kernel.h>
      14             : #include <linux/module.h>
      15             : #include <linux/interrupt.h>
      16             : #include <linux/socket.h>
      17             : #include <linux/rtnetlink.h>
      18             : #include <linux/gen_stats.h>
      19             : #include <net/netlink.h>
      20             : #include <net/gen_stats.h>
      21             : 
      22             : 
      23             : static inline int
      24           0 : gnet_stats_copy(struct gnet_dump *d, int type, void *buf, int size, int padattr)
      25             : {
      26           0 :         if (nla_put_64bit(d->skb, type, size, buf, padattr))
      27           0 :                 goto nla_put_failure;
      28             :         return 0;
      29             : 
      30           0 : nla_put_failure:
      31           0 :         if (d->lock)
      32           0 :                 spin_unlock_bh(d->lock);
      33           0 :         kfree(d->xstats);
      34           0 :         d->xstats = NULL;
      35           0 :         d->xstats_len = 0;
      36           0 :         return -1;
      37             : }
      38             : 
      39             : /**
      40             :  * gnet_stats_start_copy_compat - start dumping procedure in compatibility mode
      41             :  * @skb: socket buffer to put statistics TLVs into
      42             :  * @type: TLV type for top level statistic TLV
      43             :  * @tc_stats_type: TLV type for backward compatibility struct tc_stats TLV
      44             :  * @xstats_type: TLV type for backward compatibility xstats TLV
      45             :  * @lock: statistics lock
      46             :  * @d: dumping handle
      47             :  * @padattr: padding attribute
      48             :  *
      49             :  * Initializes the dumping handle, grabs the statistic lock and appends
      50             :  * an empty TLV header to the socket buffer for use a container for all
      51             :  * other statistic TLVS.
      52             :  *
      53             :  * The dumping handle is marked to be in backward compatibility mode telling
      54             :  * all gnet_stats_copy_XXX() functions to fill a local copy of struct tc_stats.
      55             :  *
      56             :  * Returns 0 on success or -1 if the room in the socket buffer was not sufficient.
      57             :  */
      58             : int
      59           0 : gnet_stats_start_copy_compat(struct sk_buff *skb, int type, int tc_stats_type,
      60             :                              int xstats_type, spinlock_t *lock,
      61             :                              struct gnet_dump *d, int padattr)
      62             :         __acquires(lock)
      63             : {
      64           0 :         memset(d, 0, sizeof(*d));
      65             : 
      66           0 :         if (type)
      67           0 :                 d->tail = (struct nlattr *)skb_tail_pointer(skb);
      68           0 :         d->skb = skb;
      69           0 :         d->compat_tc_stats = tc_stats_type;
      70           0 :         d->compat_xstats = xstats_type;
      71           0 :         d->padattr = padattr;
      72           0 :         if (lock) {
      73           0 :                 d->lock = lock;
      74           0 :                 spin_lock_bh(lock);
      75             :         }
      76           0 :         if (d->tail) {
      77           0 :                 int ret = gnet_stats_copy(d, type, NULL, 0, padattr);
      78             : 
      79             :                 /* The initial attribute added in gnet_stats_copy() may be
      80             :                  * preceded by a padding attribute, in which case d->tail will
      81             :                  * end up pointing at the padding instead of the real attribute.
      82             :                  * Fix this so gnet_stats_finish_copy() adjusts the length of
      83             :                  * the right attribute.
      84             :                  */
      85           0 :                 if (ret == 0 && d->tail->nla_type == padattr)
      86           0 :                         d->tail = (struct nlattr *)((char *)d->tail +
      87           0 :                                                     NLA_ALIGN(d->tail->nla_len));
      88           0 :                 return ret;
      89             :         }
      90             : 
      91             :         return 0;
      92             : }
      93             : EXPORT_SYMBOL(gnet_stats_start_copy_compat);
      94             : 
      95             : /**
      96             :  * gnet_stats_start_copy - start dumping procedure in compatibility mode
      97             :  * @skb: socket buffer to put statistics TLVs into
      98             :  * @type: TLV type for top level statistic TLV
      99             :  * @lock: statistics lock
     100             :  * @d: dumping handle
     101             :  * @padattr: padding attribute
     102             :  *
     103             :  * Initializes the dumping handle, grabs the statistic lock and appends
     104             :  * an empty TLV header to the socket buffer for use a container for all
     105             :  * other statistic TLVS.
     106             :  *
     107             :  * Returns 0 on success or -1 if the room in the socket buffer was not sufficient.
     108             :  */
     109             : int
     110           0 : gnet_stats_start_copy(struct sk_buff *skb, int type, spinlock_t *lock,
     111             :                       struct gnet_dump *d, int padattr)
     112             : {
     113           0 :         return gnet_stats_start_copy_compat(skb, type, 0, 0, lock, d, padattr);
     114             : }
     115             : EXPORT_SYMBOL(gnet_stats_start_copy);
     116             : 
     117             : static void
     118           0 : __gnet_stats_copy_basic_cpu(struct gnet_stats_basic_packed *bstats,
     119             :                             struct gnet_stats_basic_cpu __percpu *cpu)
     120             : {
     121           0 :         int i;
     122             : 
     123           0 :         for_each_possible_cpu(i) {
     124           0 :                 struct gnet_stats_basic_cpu *bcpu = per_cpu_ptr(cpu, i);
     125           0 :                 unsigned int start;
     126           0 :                 u64 bytes, packets;
     127             : 
     128           0 :                 do {
     129           0 :                         start = u64_stats_fetch_begin_irq(&bcpu->syncp);
     130           0 :                         bytes = bcpu->bstats.bytes;
     131           0 :                         packets = bcpu->bstats.packets;
     132           0 :                 } while (u64_stats_fetch_retry_irq(&bcpu->syncp, start));
     133             : 
     134           0 :                 bstats->bytes += bytes;
     135           0 :                 bstats->packets += packets;
     136             :         }
     137           0 : }
     138             : 
     139             : void
     140           0 : __gnet_stats_copy_basic(const seqcount_t *running,
     141             :                         struct gnet_stats_basic_packed *bstats,
     142             :                         struct gnet_stats_basic_cpu __percpu *cpu,
     143             :                         struct gnet_stats_basic_packed *b)
     144             : {
     145           0 :         unsigned int seq;
     146             : 
     147           0 :         if (cpu) {
     148           0 :                 __gnet_stats_copy_basic_cpu(bstats, cpu);
     149           0 :                 return;
     150             :         }
     151           0 :         do {
     152           0 :                 if (running)
     153           0 :                         seq = read_seqcount_begin(running);
     154           0 :                 bstats->bytes = b->bytes;
     155           0 :                 bstats->packets = b->packets;
     156           0 :         } while (running && read_seqcount_retry(running, seq));
     157             : }
     158             : EXPORT_SYMBOL(__gnet_stats_copy_basic);
     159             : 
     160             : static int
     161           0 : ___gnet_stats_copy_basic(const seqcount_t *running,
     162             :                          struct gnet_dump *d,
     163             :                          struct gnet_stats_basic_cpu __percpu *cpu,
     164             :                          struct gnet_stats_basic_packed *b,
     165             :                          int type)
     166             : {
     167           0 :         struct gnet_stats_basic_packed bstats = {0};
     168             : 
     169           0 :         __gnet_stats_copy_basic(running, &bstats, cpu, b);
     170             : 
     171           0 :         if (d->compat_tc_stats && type == TCA_STATS_BASIC) {
     172           0 :                 d->tc_stats.bytes = bstats.bytes;
     173           0 :                 d->tc_stats.packets = bstats.packets;
     174             :         }
     175             : 
     176           0 :         if (d->tail) {
     177           0 :                 struct gnet_stats_basic sb;
     178           0 :                 int res;
     179             : 
     180           0 :                 memset(&sb, 0, sizeof(sb));
     181           0 :                 sb.bytes = bstats.bytes;
     182           0 :                 sb.packets = bstats.packets;
     183           0 :                 res = gnet_stats_copy(d, type, &sb, sizeof(sb), TCA_STATS_PAD);
     184           0 :                 if (res < 0 || sb.packets == bstats.packets)
     185             :                         return res;
     186             :                 /* emit 64bit stats only if needed */
     187           0 :                 return gnet_stats_copy(d, TCA_STATS_PKT64, &bstats.packets,
     188             :                                        sizeof(bstats.packets), TCA_STATS_PAD);
     189             :         }
     190             :         return 0;
     191             : }
     192             : 
     193             : /**
     194             :  * gnet_stats_copy_basic - copy basic statistics into statistic TLV
     195             :  * @running: seqcount_t pointer
     196             :  * @d: dumping handle
     197             :  * @cpu: copy statistic per cpu
     198             :  * @b: basic statistics
     199             :  *
     200             :  * Appends the basic statistics to the top level TLV created by
     201             :  * gnet_stats_start_copy().
     202             :  *
     203             :  * Returns 0 on success or -1 with the statistic lock released
     204             :  * if the room in the socket buffer was not sufficient.
     205             :  */
     206             : int
     207           0 : gnet_stats_copy_basic(const seqcount_t *running,
     208             :                       struct gnet_dump *d,
     209             :                       struct gnet_stats_basic_cpu __percpu *cpu,
     210             :                       struct gnet_stats_basic_packed *b)
     211             : {
     212           0 :         return ___gnet_stats_copy_basic(running, d, cpu, b,
     213             :                                         TCA_STATS_BASIC);
     214             : }
     215             : EXPORT_SYMBOL(gnet_stats_copy_basic);
     216             : 
     217             : /**
     218             :  * gnet_stats_copy_basic_hw - copy basic hw statistics into statistic TLV
     219             :  * @running: seqcount_t pointer
     220             :  * @d: dumping handle
     221             :  * @cpu: copy statistic per cpu
     222             :  * @b: basic statistics
     223             :  *
     224             :  * Appends the basic statistics to the top level TLV created by
     225             :  * gnet_stats_start_copy().
     226             :  *
     227             :  * Returns 0 on success or -1 with the statistic lock released
     228             :  * if the room in the socket buffer was not sufficient.
     229             :  */
     230             : int
     231           0 : gnet_stats_copy_basic_hw(const seqcount_t *running,
     232             :                          struct gnet_dump *d,
     233             :                          struct gnet_stats_basic_cpu __percpu *cpu,
     234             :                          struct gnet_stats_basic_packed *b)
     235             : {
     236           0 :         return ___gnet_stats_copy_basic(running, d, cpu, b,
     237             :                                         TCA_STATS_BASIC_HW);
     238             : }
     239             : EXPORT_SYMBOL(gnet_stats_copy_basic_hw);
     240             : 
     241             : /**
     242             :  * gnet_stats_copy_rate_est - copy rate estimator statistics into statistics TLV
     243             :  * @d: dumping handle
     244             :  * @rate_est: rate estimator
     245             :  *
     246             :  * Appends the rate estimator statistics to the top level TLV created by
     247             :  * gnet_stats_start_copy().
     248             :  *
     249             :  * Returns 0 on success or -1 with the statistic lock released
     250             :  * if the room in the socket buffer was not sufficient.
     251             :  */
     252             : int
     253           0 : gnet_stats_copy_rate_est(struct gnet_dump *d,
     254             :                          struct net_rate_estimator __rcu **rate_est)
     255             : {
     256           0 :         struct gnet_stats_rate_est64 sample;
     257           0 :         struct gnet_stats_rate_est est;
     258           0 :         int res;
     259             : 
     260           0 :         if (!gen_estimator_read(rate_est, &sample))
     261             :                 return 0;
     262           0 :         est.bps = min_t(u64, UINT_MAX, sample.bps);
     263             :         /* we have some time before reaching 2^32 packets per second */
     264           0 :         est.pps = sample.pps;
     265             : 
     266           0 :         if (d->compat_tc_stats) {
     267           0 :                 d->tc_stats.bps = est.bps;
     268           0 :                 d->tc_stats.pps = est.pps;
     269             :         }
     270             : 
     271           0 :         if (d->tail) {
     272           0 :                 res = gnet_stats_copy(d, TCA_STATS_RATE_EST, &est, sizeof(est),
     273             :                                       TCA_STATS_PAD);
     274           0 :                 if (res < 0 || est.bps == sample.bps)
     275             :                         return res;
     276             :                 /* emit 64bit stats only if needed */
     277           0 :                 return gnet_stats_copy(d, TCA_STATS_RATE_EST64, &sample,
     278             :                                        sizeof(sample), TCA_STATS_PAD);
     279             :         }
     280             : 
     281             :         return 0;
     282             : }
     283             : EXPORT_SYMBOL(gnet_stats_copy_rate_est);
     284             : 
     285             : static void
     286           0 : __gnet_stats_copy_queue_cpu(struct gnet_stats_queue *qstats,
     287             :                             const struct gnet_stats_queue __percpu *q)
     288             : {
     289           0 :         int i;
     290             : 
     291           0 :         for_each_possible_cpu(i) {
     292           0 :                 const struct gnet_stats_queue *qcpu = per_cpu_ptr(q, i);
     293             : 
     294           0 :                 qstats->qlen = 0;
     295           0 :                 qstats->backlog += qcpu->backlog;
     296           0 :                 qstats->drops += qcpu->drops;
     297           0 :                 qstats->requeues += qcpu->requeues;
     298           0 :                 qstats->overlimits += qcpu->overlimits;
     299             :         }
     300           0 : }
     301             : 
     302           0 : void __gnet_stats_copy_queue(struct gnet_stats_queue *qstats,
     303             :                              const struct gnet_stats_queue __percpu *cpu,
     304             :                              const struct gnet_stats_queue *q,
     305             :                              __u32 qlen)
     306             : {
     307           0 :         if (cpu) {
     308           0 :                 __gnet_stats_copy_queue_cpu(qstats, cpu);
     309             :         } else {
     310           0 :                 qstats->qlen = q->qlen;
     311           0 :                 qstats->backlog = q->backlog;
     312           0 :                 qstats->drops = q->drops;
     313           0 :                 qstats->requeues = q->requeues;
     314           0 :                 qstats->overlimits = q->overlimits;
     315             :         }
     316             : 
     317           0 :         qstats->qlen = qlen;
     318           0 : }
     319             : EXPORT_SYMBOL(__gnet_stats_copy_queue);
     320             : 
     321             : /**
     322             :  * gnet_stats_copy_queue - copy queue statistics into statistics TLV
     323             :  * @d: dumping handle
     324             :  * @cpu_q: per cpu queue statistics
     325             :  * @q: queue statistics
     326             :  * @qlen: queue length statistics
     327             :  *
     328             :  * Appends the queue statistics to the top level TLV created by
     329             :  * gnet_stats_start_copy(). Using per cpu queue statistics if
     330             :  * they are available.
     331             :  *
     332             :  * Returns 0 on success or -1 with the statistic lock released
     333             :  * if the room in the socket buffer was not sufficient.
     334             :  */
     335             : int
     336           0 : gnet_stats_copy_queue(struct gnet_dump *d,
     337             :                       struct gnet_stats_queue __percpu *cpu_q,
     338             :                       struct gnet_stats_queue *q, __u32 qlen)
     339             : {
     340           0 :         struct gnet_stats_queue qstats = {0};
     341             : 
     342           0 :         __gnet_stats_copy_queue(&qstats, cpu_q, q, qlen);
     343             : 
     344           0 :         if (d->compat_tc_stats) {
     345           0 :                 d->tc_stats.drops = qstats.drops;
     346           0 :                 d->tc_stats.qlen = qstats.qlen;
     347           0 :                 d->tc_stats.backlog = qstats.backlog;
     348           0 :                 d->tc_stats.overlimits = qstats.overlimits;
     349             :         }
     350             : 
     351           0 :         if (d->tail)
     352           0 :                 return gnet_stats_copy(d, TCA_STATS_QUEUE,
     353             :                                        &qstats, sizeof(qstats),
     354             :                                        TCA_STATS_PAD);
     355             : 
     356             :         return 0;
     357             : }
     358             : EXPORT_SYMBOL(gnet_stats_copy_queue);
     359             : 
     360             : /**
     361             :  * gnet_stats_copy_app - copy application specific statistics into statistics TLV
     362             :  * @d: dumping handle
     363             :  * @st: application specific statistics data
     364             :  * @len: length of data
     365             :  *
     366             :  * Appends the application specific statistics to the top level TLV created by
     367             :  * gnet_stats_start_copy() and remembers the data for XSTATS if the dumping
     368             :  * handle is in backward compatibility mode.
     369             :  *
     370             :  * Returns 0 on success or -1 with the statistic lock released
     371             :  * if the room in the socket buffer was not sufficient.
     372             :  */
     373             : int
     374           0 : gnet_stats_copy_app(struct gnet_dump *d, void *st, int len)
     375             : {
     376           0 :         if (d->compat_xstats) {
     377           0 :                 d->xstats = kmemdup(st, len, GFP_ATOMIC);
     378           0 :                 if (!d->xstats)
     379           0 :                         goto err_out;
     380           0 :                 d->xstats_len = len;
     381             :         }
     382             : 
     383           0 :         if (d->tail)
     384           0 :                 return gnet_stats_copy(d, TCA_STATS_APP, st, len,
     385             :                                        TCA_STATS_PAD);
     386             : 
     387             :         return 0;
     388             : 
     389           0 : err_out:
     390           0 :         if (d->lock)
     391           0 :                 spin_unlock_bh(d->lock);
     392           0 :         d->xstats_len = 0;
     393           0 :         return -1;
     394             : }
     395             : EXPORT_SYMBOL(gnet_stats_copy_app);
     396             : 
     397             : /**
     398             :  * gnet_stats_finish_copy - finish dumping procedure
     399             :  * @d: dumping handle
     400             :  *
     401             :  * Corrects the length of the top level TLV to include all TLVs added
     402             :  * by gnet_stats_copy_XXX() calls. Adds the backward compatibility TLVs
     403             :  * if gnet_stats_start_copy_compat() was used and releases the statistics
     404             :  * lock.
     405             :  *
     406             :  * Returns 0 on success or -1 with the statistic lock released
     407             :  * if the room in the socket buffer was not sufficient.
     408             :  */
     409             : int
     410           0 : gnet_stats_finish_copy(struct gnet_dump *d)
     411             : {
     412           0 :         if (d->tail)
     413           0 :                 d->tail->nla_len = skb_tail_pointer(d->skb) - (u8 *)d->tail;
     414             : 
     415           0 :         if (d->compat_tc_stats)
     416           0 :                 if (gnet_stats_copy(d, d->compat_tc_stats, &d->tc_stats,
     417             :                                     sizeof(d->tc_stats), d->padattr) < 0)
     418             :                         return -1;
     419             : 
     420           0 :         if (d->compat_xstats && d->xstats) {
     421           0 :                 if (gnet_stats_copy(d, d->compat_xstats, d->xstats,
     422             :                                     d->xstats_len, d->padattr) < 0)
     423             :                         return -1;
     424             :         }
     425             : 
     426           0 :         if (d->lock)
     427           0 :                 spin_unlock_bh(d->lock);
     428           0 :         kfree(d->xstats);
     429           0 :         d->xstats = NULL;
     430           0 :         d->xstats_len = 0;
     431           0 :         return 0;
     432             : }
     433             : EXPORT_SYMBOL(gnet_stats_finish_copy);

Generated by: LCOV version 1.14