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

Generated by: LCOV version 1.14