LCOV - code coverage report
Current view: top level - lib/latencystats - rte_latencystats.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 84 116 72.4 %
Date: 2025-05-01 17:49:45 Functions: 9 9 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 44 68 64.7 %

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

Generated by: LCOV version 1.14