LCOV - code coverage report
Current view: top level - lib/latencystats - rte_latencystats.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 110 130 84.6 %
Date: 2025-08-01 17:49:26 Functions: 10 10 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 60 78 76.9 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  * Copyright(c) 2018 Intel Corporation
       3                 :            :  */
       4                 :            : 
       5                 :            : #include <errno.h>
       6                 :            : #include <math.h>
       7                 :            : #include <stdbool.h>
       8                 :            : #include <stddef.h>
       9                 :            : #include <stdlib.h>
      10                 :            : #include <string.h>
      11                 :            : 
      12                 :            : #include <eal_export.h>
      13                 :            : #include <rte_cycles.h>
      14                 :            : #include <rte_eal.h>
      15                 :            : #include <rte_errno.h>
      16                 :            : #include <rte_ethdev.h>
      17                 :            : #include <rte_lcore.h>
      18                 :            : #include <rte_log.h>
      19                 :            : #include <rte_mbuf.h>
      20                 :            : #include <rte_mbuf_dyn.h>
      21                 :            : #include <rte_memzone.h>
      22                 :            : #include <rte_metrics.h>
      23                 :            : #include <rte_spinlock.h>
      24                 :            : #include <rte_string_fns.h>
      25                 :            : #include <rte_stdatomic.h>
      26                 :            : 
      27                 :            : #include "rte_latencystats.h"
      28                 :            : 
      29                 :            : /** Nano seconds per second */
      30                 :            : #define NS_PER_SEC 1E9
      31                 :            : 
      32                 :            : static double cycles_per_ns;
      33                 :            : 
      34         [ -  + ]:        253 : static RTE_LOG_REGISTER_DEFAULT(latencystat_logtype, INFO);
      35                 :            : #define RTE_LOGTYPE_LATENCY_STATS latencystat_logtype
      36                 :            : #define LATENCY_STATS_LOG(level, ...) \
      37                 :            :         RTE_LOG_LINE(level, LATENCY_STATS, "" __VA_ARGS__)
      38                 :            : 
      39                 :            : static uint64_t timestamp_dynflag;
      40                 :            : static int timestamp_dynfield_offset = -1;
      41                 :            : 
      42                 :            : static inline rte_mbuf_timestamp_t *
      43                 :            : timestamp_dynfield(struct rte_mbuf *mbuf)
      44                 :            : {
      45                 :      40195 :         return RTE_MBUF_DYNFIELD(mbuf,
      46                 :            :                         timestamp_dynfield_offset, rte_mbuf_timestamp_t *);
      47                 :            : }
      48                 :            : 
      49                 :            : /* Compare two 64 bit timer counter but deal with wraparound correctly. */
      50                 :            : static inline bool tsc_after(uint64_t t0, uint64_t t1)
      51                 :            : {
      52                 :       4024 :         return (int64_t)(t1 - t0) < 0;
      53                 :            : }
      54                 :            : 
      55                 :            : #define tsc_before(a, b) tsc_after(b, a)
      56                 :            : 
      57                 :            : static const char *MZ_RTE_LATENCY_STATS = "rte_latencystats";
      58                 :            : static int latency_stats_index;
      59                 :            : 
      60                 :            : static rte_spinlock_t sample_lock = RTE_SPINLOCK_INITIALIZER;
      61                 :            : static uint64_t samp_intvl;
      62                 :            : static RTE_ATOMIC(uint64_t) next_tsc;
      63                 :            : 
      64                 :            : #define LATENCY_AVG_SCALE     4
      65                 :            : #define LATENCY_JITTER_SCALE 16
      66                 :            : 
      67                 :            : struct rte_latency_stats {
      68                 :            :         uint64_t min_latency; /**< Minimum latency */
      69                 :            :         uint64_t avg_latency; /**< Average latency */
      70                 :            :         uint64_t max_latency; /**< Maximum latency */
      71                 :            :         uint64_t jitter; /** Latency variation */
      72                 :            :         uint64_t samples;    /** Number of latency samples */
      73                 :            :         rte_spinlock_t lock; /** Latency calculation lock */
      74                 :            : };
      75                 :            : 
      76                 :            : static struct rte_latency_stats *glob_stats;
      77                 :            : 
      78                 :            : struct rxtx_cbs {
      79                 :            :         const struct rte_eth_rxtx_callback *cb;
      80                 :            : };
      81                 :            : 
      82                 :            : static struct rxtx_cbs rx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];
      83                 :            : static struct rxtx_cbs tx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];
      84                 :            : 
      85                 :            : struct latency_stats_nameoff {
      86                 :            :         char name[RTE_ETH_XSTATS_NAME_SIZE];
      87                 :            :         unsigned int offset;
      88                 :            :         unsigned int scale;
      89                 :            : };
      90                 :            : 
      91                 :            : static const struct latency_stats_nameoff lat_stats_strings[] = {
      92                 :            :         {"min_latency_ns", offsetof(struct rte_latency_stats, min_latency), 1},
      93                 :            :         {"avg_latency_ns", offsetof(struct rte_latency_stats, avg_latency), LATENCY_AVG_SCALE},
      94                 :            :         {"max_latency_ns", offsetof(struct rte_latency_stats, max_latency), 1},
      95                 :            :         {"jitter_ns", offsetof(struct rte_latency_stats, jitter), LATENCY_JITTER_SCALE},
      96                 :            :         {"samples", offsetof(struct rte_latency_stats, samples), 0},
      97                 :            : };
      98                 :            : 
      99                 :            : #define NUM_LATENCY_STATS RTE_DIM(lat_stats_strings)
     100                 :            : 
     101                 :            : static void
     102                 :          4 : latencystats_collect(uint64_t values[])
     103                 :            : {
     104                 :            :         unsigned int i, scale;
     105                 :            :         const uint64_t *stats;
     106                 :            : 
     107         [ +  + ]:         24 :         for (i = 0; i < NUM_LATENCY_STATS; i++) {
     108                 :         20 :                 stats = RTE_PTR_ADD(glob_stats, lat_stats_strings[i].offset);
     109                 :         20 :                 scale = lat_stats_strings[i].scale;
     110                 :            : 
     111                 :            :                 /* used to mark samples which are not a time interval */
     112         [ +  + ]:         20 :                 if (scale == 0)
     113                 :          4 :                         values[i] = *stats;
     114                 :            :                 else
     115                 :         16 :                         values[i] = floor(*stats / (cycles_per_ns * scale));
     116                 :            :         }
     117                 :          4 : }
     118                 :            : 
     119                 :            : RTE_EXPORT_SYMBOL(rte_latencystats_update)
     120                 :            : int32_t
     121                 :          1 : rte_latencystats_update(void)
     122                 :            : {
     123                 :            :         uint64_t values[NUM_LATENCY_STATS];
     124                 :            :         int ret;
     125                 :            : 
     126                 :          1 :         latencystats_collect(values);
     127                 :            : 
     128                 :          1 :         ret = rte_metrics_update_values(RTE_METRICS_GLOBAL,
     129                 :            :                                         latency_stats_index,
     130                 :            :                                         values, NUM_LATENCY_STATS);
     131         [ -  + ]:          1 :         if (ret < 0)
     132                 :          0 :                 LATENCY_STATS_LOG(INFO, "Failed to push the stats");
     133                 :            : 
     134                 :          1 :         return ret;
     135                 :            : }
     136                 :            : 
     137                 :            : static void
     138                 :          3 : rte_latencystats_fill_values(struct rte_metric_value *metrics)
     139                 :            : {
     140                 :            :         uint64_t values[NUM_LATENCY_STATS];
     141                 :            :         unsigned int i;
     142                 :            : 
     143                 :          3 :         latencystats_collect(values);
     144                 :            : 
     145         [ +  + ]:         18 :         for (i = 0; i < NUM_LATENCY_STATS; i++) {
     146                 :         15 :                 metrics[i].key = i;
     147                 :         15 :                 metrics[i].value = values[i];
     148                 :            :         }
     149                 :          3 : }
     150                 :            : 
     151                 :            : static uint16_t
     152                 :       4024 : add_time_stamps(uint16_t pid __rte_unused,
     153                 :            :                 uint16_t qid __rte_unused,
     154                 :            :                 struct rte_mbuf **pkts,
     155                 :            :                 uint16_t nb_pkts,
     156                 :            :                 uint16_t max_pkts __rte_unused,
     157                 :            :                 void *user_cb __rte_unused)
     158                 :            : {
     159                 :            :         unsigned int i;
     160                 :            :         uint64_t now = rte_rdtsc();
     161                 :            : 
     162                 :            :         /* Check without locking */
     163         [ +  - ]:       4024 :         if (likely(tsc_before(now, rte_atomic_load_explicit(&next_tsc,
     164                 :            :                                                             rte_memory_order_relaxed))))
     165                 :            :                 return nb_pkts;
     166                 :            : 
     167                 :            :         /* Try and get sample, skip if sample is being done by other core. */
     168         [ +  - ]:       4024 :         if (likely(rte_spinlock_trylock(&sample_lock))) {
     169         [ +  + ]:      44209 :                 for (i = 0; i < nb_pkts; i++) {
     170                 :      40195 :                         struct rte_mbuf *m = pkts[i];
     171                 :            : 
     172                 :            :                         /* skip if already timestamped */
     173         [ +  + ]:      40195 :                         if (unlikely(m->ol_flags & timestamp_dynflag))
     174                 :            :                                 continue;
     175                 :            : 
     176                 :         10 :                         m->ol_flags |= timestamp_dynflag;
     177                 :         10 :                         *timestamp_dynfield(m) = now;
     178                 :         10 :                         rte_atomic_store_explicit(&next_tsc, now + samp_intvl,
     179                 :            :                                                   rte_memory_order_relaxed);
     180                 :         10 :                         break;
     181                 :            :                 }
     182                 :            :                 rte_spinlock_unlock(&sample_lock);
     183                 :            :         }
     184                 :            : 
     185                 :            :         return nb_pkts;
     186                 :            : }
     187                 :            : 
     188                 :            : static uint16_t
     189                 :       4024 : calc_latency(uint16_t pid __rte_unused,
     190                 :            :                 uint16_t qid __rte_unused,
     191                 :            :                 struct rte_mbuf **pkts,
     192                 :            :                 uint16_t nb_pkts,
     193                 :            :                 void *_ __rte_unused)
     194                 :            : {
     195                 :            :         unsigned int i;
     196                 :            :         uint64_t now, latency;
     197                 :            :         uint64_t ts_flags = 0;
     198                 :            :         static uint64_t prev_latency;
     199                 :            : 
     200         [ +  + ]:      44264 :         for (i = 0; i < nb_pkts; i++)
     201                 :      40240 :                 ts_flags |= (pkts[i]->ol_flags & timestamp_dynflag);
     202                 :            : 
     203                 :            :         /* no samples in this burst, skip locking */
     204         [ +  + ]:       4024 :         if (likely(ts_flags == 0))
     205                 :            :                 return nb_pkts;
     206                 :            : 
     207                 :            :         now = rte_rdtsc();
     208                 :       4023 :         rte_spinlock_lock(&glob_stats->lock);
     209         [ +  + ]:      44253 :         for (i = 0; i < nb_pkts; i++) {
     210         [ +  + ]:      40230 :                 if (!(pkts[i]->ol_flags & timestamp_dynflag))
     211                 :         45 :                         continue;
     212                 :            : 
     213                 :      40185 :                 latency = now - *timestamp_dynfield(pkts[i]);
     214                 :            : 
     215         [ +  + ]:      40185 :                 if (glob_stats->samples++ == 0) {
     216                 :          1 :                         glob_stats->min_latency = latency;
     217                 :          1 :                         glob_stats->max_latency = latency;
     218                 :          1 :                         glob_stats->avg_latency = latency * 4;
     219                 :            :                         /* start ad if previous sample had 0 latency */
     220                 :          1 :                         glob_stats->jitter = latency / LATENCY_JITTER_SCALE;
     221                 :            :                 } else {
     222                 :            :                         /*
     223                 :            :                          * The jitter is calculated as statistical mean of interpacket
     224                 :            :                          * delay variation. The "jitter estimate" is computed by taking
     225                 :            :                          * the absolute values of the ipdv sequence and applying an
     226                 :            :                          * exponential filter with parameter 1/16 to generate the
     227                 :            :                          * estimate. i.e J=J+(|D(i-1,i)|-J)/16. Where J is jitter,
     228                 :            :                          * D(i-1,i) is difference in latency of two consecutive packets
     229                 :            :                          * i-1 and i. Jitter is scaled by 16.
     230                 :            :                          * Reference: Calculated as per RFC 5481, sec 4.1,
     231                 :            :                          * RFC 3393 sec 4.5, RFC 1889 sec.
     232                 :            :                          */
     233                 :      40184 :                         long long delta = prev_latency - latency;
     234                 :      40184 :                         glob_stats->jitter += llabs(delta)
     235                 :      40184 :                                 - glob_stats->jitter / LATENCY_JITTER_SCALE;
     236                 :            : 
     237         [ +  + ]:      40184 :                         if (latency < glob_stats->min_latency)
     238                 :          2 :                                 glob_stats->min_latency = latency;
     239         [ +  + ]:      40184 :                         if (latency > glob_stats->max_latency)
     240                 :       4022 :                                 glob_stats->max_latency = latency;
     241                 :            :                         /*
     242                 :            :                          * The average latency is measured using exponential moving
     243                 :            :                          * average, i.e. using EWMA
     244                 :            :                          * https://en.wikipedia.org/wiki/Moving_average
     245                 :            :                          *
     246                 :            :                          * Alpha is .25, avg_latency is scaled by 4.
     247                 :            :                          */
     248                 :      40184 :                         glob_stats->avg_latency += latency
     249                 :      40184 :                                 - glob_stats->avg_latency / LATENCY_AVG_SCALE;
     250                 :            :                 }
     251                 :            : 
     252                 :      40185 :                 prev_latency = latency;
     253                 :            :         }
     254                 :       4023 :         rte_spinlock_unlock(&glob_stats->lock);
     255                 :            : 
     256                 :       4023 :         return nb_pkts;
     257                 :            : }
     258                 :            : 
     259                 :            : RTE_EXPORT_SYMBOL(rte_latencystats_init)
     260                 :            : int
     261                 :          1 : rte_latencystats_init(uint64_t app_samp_intvl,
     262                 :            :                 rte_latency_stats_flow_type_fn user_cb)
     263                 :            : {
     264                 :            :         unsigned int i;
     265                 :            :         uint16_t pid;
     266                 :            :         uint16_t qid;
     267                 :            :         struct rxtx_cbs *cbs = NULL;
     268                 :          1 :         const char *ptr_strings[NUM_LATENCY_STATS] = {0};
     269                 :            :         const struct rte_memzone *mz = NULL;
     270                 :            :         const unsigned int flags = 0;
     271                 :            :         int ret;
     272                 :            : 
     273         [ +  - ]:          1 :         if (rte_memzone_lookup(MZ_RTE_LATENCY_STATS))
     274                 :            :                 return -EEXIST;
     275                 :            : 
     276                 :            :         /** Reserved for possible future use */
     277         [ +  - ]:          1 :         if (user_cb != NULL)
     278                 :            :                 return -ENOTSUP;
     279                 :            : 
     280                 :            :         /** Allocate stats in shared memory fo multi process support */
     281                 :          1 :         mz = rte_memzone_reserve(MZ_RTE_LATENCY_STATS, sizeof(*glob_stats),
     282                 :          1 :                                         rte_socket_id(), flags);
     283         [ -  + ]:          1 :         if (mz == NULL) {
     284                 :          0 :                 LATENCY_STATS_LOG(ERR, "Cannot reserve memory: %s:%d",
     285                 :            :                         __func__, __LINE__);
     286                 :          0 :                 return -ENOMEM;
     287                 :            :         }
     288                 :            : 
     289                 :          1 :         cycles_per_ns = (double)rte_get_tsc_hz() / NS_PER_SEC;
     290                 :            : 
     291                 :          1 :         glob_stats = mz->addr;
     292                 :            :         rte_spinlock_init(&glob_stats->lock);
     293                 :          1 :         samp_intvl = (uint64_t)(app_samp_intvl * cycles_per_ns);
     294                 :          1 :         next_tsc = rte_rdtsc();
     295                 :            : 
     296                 :            :         /** Register latency stats with stats library */
     297         [ +  + ]:          6 :         for (i = 0; i < NUM_LATENCY_STATS; i++)
     298                 :          5 :                 ptr_strings[i] = lat_stats_strings[i].name;
     299                 :            : 
     300                 :          1 :         latency_stats_index = rte_metrics_reg_names(ptr_strings,
     301                 :            :                                                         NUM_LATENCY_STATS);
     302         [ -  + ]:          1 :         if (latency_stats_index < 0) {
     303                 :          0 :                 LATENCY_STATS_LOG(ERR,
     304                 :            :                         "Failed to register latency stats names");
     305                 :          0 :                 return -1;
     306                 :            :         }
     307                 :            : 
     308                 :            :         /* Register mbuf field and flag for Rx timestamp */
     309                 :          1 :         ret = rte_mbuf_dyn_rx_timestamp_register(&timestamp_dynfield_offset,
     310                 :            :                         &timestamp_dynflag);
     311         [ -  + ]:          1 :         if (ret != 0) {
     312                 :          0 :                 LATENCY_STATS_LOG(ERR,
     313                 :            :                         "Cannot register mbuf field/flag for timestamp");
     314                 :          0 :                 return -rte_errno;
     315                 :            :         }
     316                 :            : 
     317                 :            :         /** Register Rx/Tx callbacks */
     318         [ +  + ]:          2 :         RTE_ETH_FOREACH_DEV(pid) {
     319                 :            :                 struct rte_eth_dev_info dev_info;
     320                 :            : 
     321                 :          1 :                 ret = rte_eth_dev_info_get(pid, &dev_info);
     322         [ -  + ]:          1 :                 if (ret != 0) {
     323                 :          0 :                         LATENCY_STATS_LOG(NOTICE,
     324                 :            :                                 "Can not get info for device (port %u): %s",
     325                 :            :                                 pid, strerror(-ret));
     326                 :            : 
     327                 :          0 :                         continue;
     328                 :            :                 }
     329                 :            : 
     330         [ +  + ]:          2 :                 for (qid = 0; qid < dev_info.nb_rx_queues; qid++) {
     331                 :          1 :                         cbs = &rx_cbs[pid][qid];
     332                 :          1 :                         cbs->cb = rte_eth_add_first_rx_callback(pid, qid,
     333                 :            :                                         add_time_stamps, NULL);
     334         [ -  + ]:          1 :                         if (!cbs->cb)
     335                 :          0 :                                 LATENCY_STATS_LOG(NOTICE,
     336                 :            :                                         "Failed to register Rx callback for pid=%u, qid=%u",
     337                 :            :                                         pid, qid);
     338                 :            :                 }
     339         [ +  + ]:          2 :                 for (qid = 0; qid < dev_info.nb_tx_queues; qid++) {
     340                 :          1 :                         cbs = &tx_cbs[pid][qid];
     341                 :          1 :                         cbs->cb =  rte_eth_add_tx_callback(pid, qid,
     342                 :            :                                         calc_latency, NULL);
     343         [ -  + ]:          1 :                         if (!cbs->cb)
     344                 :          0 :                                 LATENCY_STATS_LOG(NOTICE,
     345                 :            :                                         "Failed to register Tx callback for pid=%u, qid=%u",
     346                 :            :                                         pid, qid);
     347                 :            :                 }
     348                 :            :         }
     349                 :            :         return 0;
     350                 :            : }
     351                 :            : 
     352                 :            : RTE_EXPORT_SYMBOL(rte_latencystats_uninit)
     353                 :            : int
     354                 :          1 : rte_latencystats_uninit(void)
     355                 :            : {
     356                 :            :         uint16_t pid;
     357                 :            :         uint16_t qid;
     358                 :            :         int ret = 0;
     359                 :            :         struct rxtx_cbs *cbs = NULL;
     360                 :            :         const struct rte_memzone *mz = NULL;
     361                 :            : 
     362                 :            :         /** De register Rx/Tx callbacks */
     363         [ +  + ]:          2 :         RTE_ETH_FOREACH_DEV(pid) {
     364                 :            :                 struct rte_eth_dev_info dev_info;
     365                 :            : 
     366                 :          1 :                 ret = rte_eth_dev_info_get(pid, &dev_info);
     367         [ +  - ]:          1 :                 if (ret != 0) {
     368                 :          0 :                         LATENCY_STATS_LOG(NOTICE,
     369                 :            :                                 "Can not get info for device (port %u): %s",
     370                 :            :                                 pid, strerror(-ret));
     371                 :          0 :                         continue;
     372                 :            :                 }
     373                 :            : 
     374         [ +  + ]:          2 :                 for (qid = 0; qid < dev_info.nb_rx_queues; qid++) {
     375                 :          1 :                         cbs = &rx_cbs[pid][qid];
     376                 :          1 :                         ret = rte_eth_remove_rx_callback(pid, qid, cbs->cb);
     377         [ -  + ]:          1 :                         if (ret)
     378                 :          0 :                                 LATENCY_STATS_LOG(NOTICE,
     379                 :            :                                         "Failed to remove Rx callback for pid=%u, qid=%u",
     380                 :            :                                         pid, qid);
     381                 :            :                 }
     382         [ +  + ]:          2 :                 for (qid = 0; qid < dev_info.nb_tx_queues; qid++) {
     383                 :          1 :                         cbs = &tx_cbs[pid][qid];
     384                 :          1 :                         ret = rte_eth_remove_tx_callback(pid, qid, cbs->cb);
     385         [ -  + ]:          1 :                         if (ret)
     386                 :          0 :                                 LATENCY_STATS_LOG(NOTICE,
     387                 :            :                                         "Failed to remove Tx callback for pid=%u, qid=%u",
     388                 :            :                                         pid, qid);
     389                 :            :                 }
     390                 :            :         }
     391                 :            : 
     392                 :            :         /* free up the memzone */
     393                 :          1 :         mz = rte_memzone_lookup(MZ_RTE_LATENCY_STATS);
     394                 :          1 :         rte_memzone_free(mz);
     395                 :            : 
     396                 :          1 :         return 0;
     397                 :            : }
     398                 :            : 
     399                 :            : RTE_EXPORT_SYMBOL(rte_latencystats_get_names)
     400                 :            : int
     401                 :          4 : rte_latencystats_get_names(struct rte_metric_name *names, uint16_t size)
     402                 :            : {
     403                 :            :         unsigned int i;
     404                 :            : 
     405         [ +  + ]:          4 :         if (names == NULL || size < NUM_LATENCY_STATS)
     406                 :            :                 return NUM_LATENCY_STATS;
     407                 :            : 
     408         [ +  + ]:         12 :         for (i = 0; i < NUM_LATENCY_STATS; i++)
     409                 :         10 :                 strlcpy(names[i].name, lat_stats_strings[i].name,
     410                 :            :                         sizeof(names[i].name));
     411                 :            : 
     412                 :            :         return NUM_LATENCY_STATS;
     413                 :            : }
     414                 :            : 
     415                 :            : RTE_EXPORT_SYMBOL(rte_latencystats_get)
     416                 :            : int
     417                 :          5 : rte_latencystats_get(struct rte_metric_value *values, uint16_t size)
     418                 :            : {
     419         [ +  + ]:          5 :         if (size < NUM_LATENCY_STATS || values == NULL)
     420                 :            :                 return NUM_LATENCY_STATS;
     421                 :            : 
     422         [ -  + ]:          3 :         if (rte_eal_process_type() == RTE_PROC_SECONDARY) {
     423                 :            :                 const struct rte_memzone *mz;
     424                 :          0 :                 mz = rte_memzone_lookup(MZ_RTE_LATENCY_STATS);
     425         [ #  # ]:          0 :                 if (mz == NULL) {
     426                 :          0 :                         LATENCY_STATS_LOG(ERR,
     427                 :            :                                 "Latency stats memzone not found");
     428                 :          0 :                         return -ENOMEM;
     429                 :            :                 }
     430                 :          0 :                 glob_stats =  mz->addr;
     431                 :            :         }
     432                 :            : 
     433                 :            :         /* Retrieve latency stats */
     434                 :          3 :         rte_latencystats_fill_values(values);
     435                 :            : 
     436                 :          3 :         return NUM_LATENCY_STATS;
     437                 :            : }

Generated by: LCOV version 1.14