LCOV - code coverage report
Current view: top level - lib/latencystats - rte_latencystats.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 106 126 84.1 %
Date: 2025-07-01 21:32:37 Functions: 10 10 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 55 72 76.4 %

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

Generated by: LCOV version 1.14