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