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(×tamp_dynfield_offset,
310 : : ×tamp_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 : : }
|