1
2
3
4
5#include <ethdev_driver.h>
6#include <rte_string_fns.h>
7#ifdef RTE_LIB_TELEMETRY
8#include <telemetry_internal.h>
9#endif
10
11#include "rte_metrics.h"
12#include "rte_metrics_telemetry.h"
13
14#ifdef RTE_HAS_JANSSON
15
16struct telemetry_metrics_data tel_met_data;
17
18int metrics_log_level;
19
20
21#define METRICS_LOG(level, fmt, args...) \
22 rte_log(RTE_LOG_ ##level, metrics_log_level, "%s(): "fmt "\n", \
23 __func__, ##args)
24
25#define METRICS_LOG_ERR(fmt, args...) \
26 METRICS_LOG(ERR, fmt, ## args)
27
28#define METRICS_LOG_WARN(fmt, args...) \
29 METRICS_LOG(WARNING, fmt, ## args)
30
31static int32_t
32rte_metrics_tel_reg_port_ethdev_to_metrics(uint16_t port_id)
33{
34 int ret, num_xstats, i;
35 struct rte_eth_xstat_name *eth_xstats_names;
36 const char **xstats_names;
37
38 num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
39 if (num_xstats < 0) {
40 METRICS_LOG_ERR("rte_eth_xstats_get(%u) failed: %d",
41 port_id, num_xstats);
42 return -EPERM;
43 }
44
45 xstats_names = malloc(sizeof(*xstats_names) * num_xstats);
46 eth_xstats_names = malloc(sizeof(struct rte_eth_xstat_name)
47 * num_xstats);
48 if (eth_xstats_names == NULL || xstats_names == NULL) {
49 METRICS_LOG_ERR("Failed to malloc memory for xstats_names");
50 ret = -ENOMEM;
51 goto free_xstats;
52 }
53
54 if (rte_eth_xstats_get_names(port_id,
55 eth_xstats_names, num_xstats) != num_xstats) {
56 METRICS_LOG_ERR("rte_eth_xstats_get_names(%u) len %d failed",
57 port_id, num_xstats);
58 ret = -EPERM;
59 goto free_xstats;
60 }
61
62 for (i = 0; i < num_xstats; i++)
63 xstats_names[i] = eth_xstats_names[i].name;
64 ret = rte_metrics_reg_names(xstats_names, num_xstats);
65 if (ret < 0)
66 METRICS_LOG_ERR("rte_metrics_reg_names failed - metrics may already be registered");
67
68free_xstats:
69 free(eth_xstats_names);
70 free(xstats_names);
71 return ret;
72}
73
74int32_t
75rte_metrics_tel_reg_all_ethdev(int *metrics_register_done, int *reg_index_list)
76{
77 struct driver_index {
78 const void *dev_ops;
79 int reg_index;
80 } drv_idx[RTE_MAX_ETHPORTS] = { {0} };
81 int ret, nb_drv_idx = 0;
82 uint16_t d;
83
84 rte_metrics_init(rte_socket_id());
85 RTE_ETH_FOREACH_DEV(d) {
86 int i;
87
88
89
90
91 for (i = 0; i < nb_drv_idx; i++) {
92 if (rte_eth_devices[d].dev_ops == drv_idx[i].dev_ops) {
93 reg_index_list[d] = drv_idx[i].reg_index;
94 break;
95 }
96 }
97 if (i < nb_drv_idx)
98 continue;
99
100
101 ret = rte_metrics_tel_reg_port_ethdev_to_metrics(d);
102 if (ret < 0) {
103 METRICS_LOG_ERR("Failed to register ethdev to metrics");
104 return ret;
105 }
106 reg_index_list[d] = ret;
107 drv_idx[nb_drv_idx].dev_ops = rte_eth_devices[d].dev_ops;
108 drv_idx[nb_drv_idx].reg_index = ret;
109 nb_drv_idx++;
110 }
111 *metrics_register_done = 1;
112 return 0;
113}
114
115static int32_t
116rte_metrics_tel_update_metrics_ethdev(uint16_t port_id, int reg_start_index)
117{
118 int ret, num_xstats, i;
119 struct rte_eth_xstat *eth_xstats;
120
121 num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
122 if (num_xstats < 0) {
123 METRICS_LOG_ERR("rte_eth_xstats_get(%u) failed: %d", port_id,
124 num_xstats);
125 return -EPERM;
126 }
127 eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats);
128 if (eth_xstats == NULL) {
129 METRICS_LOG_ERR("Failed to malloc memory for xstats");
130 return -ENOMEM;
131 }
132 ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats);
133 if (ret < 0 || ret > num_xstats) {
134 free(eth_xstats);
135 METRICS_LOG_ERR("rte_eth_xstats_get(%u) len%i failed: %d",
136 port_id, num_xstats, ret);
137 return -EPERM;
138 }
139
140 uint64_t xstats_values[num_xstats];
141 for (i = 0; i < num_xstats; i++)
142 xstats_values[i] = eth_xstats[i].value;
143 if (rte_metrics_update_values(port_id, reg_start_index, xstats_values,
144 num_xstats) < 0) {
145 METRICS_LOG_ERR("Could not update metrics values");
146 free(eth_xstats);
147 return -EPERM;
148 }
149 free(eth_xstats);
150 return 0;
151}
152
153static int32_t
154rte_metrics_tel_format_port(uint32_t pid, json_t *ports,
155 uint32_t *metric_ids, int num_metric_ids)
156{
157 struct rte_metric_value *metrics = NULL;
158 struct rte_metric_name *names = NULL;
159 int num_metrics, i, ret = -EPERM;
160 json_t *port, *stats;
161
162 num_metrics = rte_metrics_get_names(NULL, 0);
163 if (num_metrics < 0) {
164 METRICS_LOG_ERR("Cannot get metrics count");
165 return -EINVAL;
166 } else if (num_metrics == 0) {
167 METRICS_LOG_ERR("No metrics to display (none have been registered)");
168 return -EPERM;
169 }
170
171 metrics = malloc(sizeof(struct rte_metric_value) * num_metrics);
172 names = malloc(sizeof(struct rte_metric_name) * num_metrics);
173 if (metrics == NULL || names == NULL) {
174 METRICS_LOG_ERR("Cannot allocate memory");
175 ret = -ENOMEM;
176 goto fail;
177 }
178
179 if (rte_metrics_get_names(names, num_metrics) != num_metrics ||
180 rte_metrics_get_values(pid, metrics, num_metrics)
181 != num_metrics) {
182 METRICS_LOG_ERR("Error getting metrics");
183 goto fail;
184 }
185
186 stats = json_array();
187 if (stats == NULL) {
188 METRICS_LOG_ERR("Could not create stats JSON object");
189 goto fail;
190 }
191
192 for (i = 0; i < num_metrics; i++) {
193 int32_t j;
194 for (j = 0; j < num_metric_ids; j++)
195 if (metrics[i].key == metric_ids[j])
196 break;
197
198 if (num_metric_ids > 0 && j == num_metric_ids)
199 continue;
200
201 json_t *stat = json_pack("{s,s,s,I}",
202 "name", names[metrics[i].key].name,
203 "value", metrics[i].value);
204 if (stat == NULL || json_array_append_new(stats, stat) < 0) {
205 METRICS_LOG_ERR("Format stat with id: %u failed",
206 metrics[i].key);
207 goto fail;
208 }
209 }
210
211 port = json_pack("{s,i,s,o}", "port", pid, "stats",
212 json_array_size(stats) ? stats : json_null());
213 if (port == NULL || json_array_append_new(ports, port) < 0) {
214 METRICS_LOG_ERR("Error creating port and adding to ports");
215 goto fail;
216 }
217
218 free(metrics);
219 free(names);
220 return 0;
221
222fail:
223 free(metrics);
224 free(names);
225 return ret;
226}
227
228int32_t
229rte_metrics_tel_encode_json_format(struct telemetry_encode_param *ep,
230 char **json_buffer)
231{
232 json_t *root, *ports;
233 int ret, i;
234
235 ports = json_array();
236 if (ports == NULL) {
237 METRICS_LOG_ERR("Could not create ports JSON array");
238 return -EPERM;
239 }
240
241 if (ep->type == PORT_STATS) {
242 if (ep->pp.num_port_ids <= 0) {
243 METRICS_LOG_ERR("Please provide port/metric ids");
244 return -EINVAL;
245 }
246
247 for (i = 0; i < ep->pp.num_port_ids; i++) {
248 ret = rte_metrics_tel_format_port(ep->pp.port_ids[i],
249 ports, &ep->pp.metric_ids[0],
250 ep->pp.num_metric_ids);
251 if (ret < 0) {
252 METRICS_LOG_ERR("Format port in JSON failed");
253 return ret;
254 }
255 }
256 } else if (ep->type == GLOBAL_STATS) {
257
258 ret = rte_metrics_tel_format_port(RTE_METRICS_GLOBAL,
259 ports, NULL, 0);
260 if (ret < 0) {
261 METRICS_LOG_ERR("Request Global Metrics Failed");
262 return ret;
263 }
264 } else {
265 METRICS_LOG_ERR("Invalid metrics type in encode params");
266 return -EINVAL;
267 }
268
269 root = json_pack("{s,s,s,o}", "status_code", "Status OK: 200",
270 "data", ports);
271 if (root == NULL) {
272 METRICS_LOG_ERR("Root, Status or data field cannot be set");
273 return -EPERM;
274 }
275
276 *json_buffer = json_dumps(root, JSON_INDENT(2));
277 json_decref(root);
278 return 0;
279}
280
281int32_t
282rte_metrics_tel_get_ports_stats_json(struct telemetry_encode_param *ep,
283 int *reg_index, char **json_buffer)
284{
285 int ret, i;
286 uint32_t port_id;
287
288 for (i = 0; i < ep->pp.num_port_ids; i++) {
289 port_id = ep->pp.port_ids[i];
290 if (!rte_eth_dev_is_valid_port(port_id)) {
291 METRICS_LOG_ERR("Port: %d invalid", port_id);
292 return -EINVAL;
293 }
294
295 ret = rte_metrics_tel_update_metrics_ethdev(port_id,
296 reg_index[i]);
297 if (ret < 0) {
298 METRICS_LOG_ERR("Failed to update ethdev metrics");
299 return ret;
300 }
301 }
302
303 ret = rte_metrics_tel_encode_json_format(ep, json_buffer);
304 if (ret < 0) {
305 METRICS_LOG_ERR("JSON encode function failed");
306 return ret;
307 }
308 return 0;
309}
310
311int32_t
312rte_metrics_tel_get_port_stats_ids(struct telemetry_encode_param *ep)
313{
314 int p, num_port_ids = 0;
315
316 RTE_ETH_FOREACH_DEV(p) {
317 ep->pp.port_ids[num_port_ids] = p;
318 num_port_ids++;
319 }
320
321 if (!num_port_ids) {
322 METRICS_LOG_ERR("No active ports");
323 return -EINVAL;
324 }
325
326 ep->pp.num_port_ids = num_port_ids;
327 ep->pp.num_metric_ids = 0;
328 ep->type = PORT_STATS;
329 return 0;
330}
331
332static int32_t
333rte_metrics_tel_stat_names_to_ids(const char * const *stat_names,
334 uint32_t *stat_ids, int num_stat_names)
335{
336 struct rte_metric_name *names;
337 int num_metrics;
338 int i, j, nb_stat_ids = 0;
339
340 num_metrics = rte_metrics_get_names(NULL, 0);
341 if (num_metrics <= 0) {
342 METRICS_LOG_ERR("Error getting metrics count - no metrics may be registered");
343 return -EPERM;
344 }
345
346 names = malloc(sizeof(struct rte_metric_name) * num_metrics);
347 if (names == NULL) {
348 METRICS_LOG_ERR("Cannot allocate memory for names");
349 return -ENOMEM;
350 }
351
352 if (rte_metrics_get_names(names, num_metrics) != num_metrics) {
353 METRICS_LOG_ERR("Cannot get metrics names");
354 free(names);
355 return -EPERM;
356 }
357
358 for (i = 0; i < num_stat_names; i++) {
359 for (j = 0; j < num_metrics; j++) {
360 if (strcmp(stat_names[i], names[j].name) == 0) {
361 stat_ids[nb_stat_ids++] = j;
362 break;
363 }
364 }
365 if (j == num_metrics) {
366 METRICS_LOG_WARN("Invalid stat name %s\n",
367 stat_names[i]);
368 free(names);
369 return -EINVAL;
370 }
371 }
372
373 free(names);
374 return 0;
375}
376
377int32_t
378rte_metrics_tel_extract_data(struct telemetry_encode_param *ep, json_t *data)
379{
380 int ret;
381 json_t *port_ids_json = json_object_get(data, "ports");
382 json_t *stat_names_json = json_object_get(data, "stats");
383 uint64_t num_stat_names = json_array_size(stat_names_json);
384 const char *stat_names[num_stat_names];
385 size_t index;
386 json_t *value;
387
388 memset(ep, 0, sizeof(*ep));
389 ep->pp.num_port_ids = json_array_size(port_ids_json);
390 ep->pp.num_metric_ids = num_stat_names;
391 if (!json_is_object(data) || !json_is_array(port_ids_json) ||
392 !json_is_array(stat_names_json)) {
393 METRICS_LOG_WARN("Invalid data provided for this command");
394 return -EINVAL;
395 }
396
397 json_array_foreach(port_ids_json, index, value) {
398 if (!json_is_integer(value)) {
399 METRICS_LOG_WARN("Port ID given is not valid");
400 return -EINVAL;
401 }
402 ep->pp.port_ids[index] = json_integer_value(value);
403 if (rte_eth_dev_is_valid_port(ep->pp.port_ids[index]) < 1)
404 return -EINVAL;
405 }
406 json_array_foreach(stat_names_json, index, value) {
407 if (!json_is_string(value)) {
408 METRICS_LOG_WARN("Stat Name given is not a string");
409 return -EINVAL;
410 }
411 stat_names[index] = json_string_value(value);
412 }
413
414 ret = rte_metrics_tel_stat_names_to_ids(stat_names, ep->pp.metric_ids,
415 num_stat_names);
416 if (ret < 0) {
417 METRICS_LOG_ERR("Could not convert stat names to IDs");
418 return ret;
419 }
420
421 ep->type = PORT_STATS;
422 return 0;
423}
424
425static int
426rte_metrics_tel_initial_metrics_setup(void)
427{
428 int ret;
429 rte_metrics_init(rte_socket_id());
430
431 if (!tel_met_data.metrics_register_done) {
432 ret = rte_metrics_tel_reg_all_ethdev(
433 &tel_met_data.metrics_register_done,
434 tel_met_data.reg_index);
435 if (ret < 0)
436 return ret;
437 }
438 return 0;
439}
440
441static int
442handle_ports_all_stats_values(const char *cmd __rte_unused,
443 const char *params __rte_unused,
444 char *buffer, int buf_len)
445{
446 struct telemetry_encode_param ep;
447 int ret, used = 0;
448 char *json_buffer = NULL;
449
450 ret = rte_metrics_tel_initial_metrics_setup();
451 if (ret < 0)
452 return ret;
453
454 memset(&ep, 0, sizeof(ep));
455 ret = rte_metrics_tel_get_port_stats_ids(&ep);
456 if (ret < 0)
457 return ret;
458
459 ret = rte_metrics_tel_get_ports_stats_json(&ep, tel_met_data.reg_index,
460 &json_buffer);
461 if (ret < 0)
462 return ret;
463
464 used += strlcpy(buffer, json_buffer, buf_len);
465 return used;
466}
467
468static int
469handle_global_stats_values(const char *cmd __rte_unused,
470 const char *params __rte_unused,
471 char *buffer, int buf_len)
472{
473 char *json_buffer = NULL;
474 struct telemetry_encode_param ep = { .type = GLOBAL_STATS };
475 int ret, used = 0;
476
477 ret = rte_metrics_tel_initial_metrics_setup();
478 if (ret < 0)
479 return ret;
480
481 ret = rte_metrics_tel_encode_json_format(&ep, &json_buffer);
482 if (ret < 0) {
483 METRICS_LOG_ERR("JSON encode function failed");
484 return ret;
485 }
486 used += strlcpy(buffer, json_buffer, buf_len);
487 return used;
488}
489
490static int
491handle_ports_stats_values_by_name(const char *cmd __rte_unused,
492 const char *params,
493 char *buffer, int buf_len)
494{
495 char *json_buffer = NULL;
496 struct telemetry_encode_param ep;
497 int ret, used = 0;
498 json_t *data;
499 json_error_t error;
500
501 ret = rte_metrics_tel_initial_metrics_setup();
502 if (ret < 0)
503 return ret;
504
505 data = json_loads(params, 0, &error);
506 if (!data) {
507 METRICS_LOG_WARN("Could not load JSON object from data passed in : %s",
508 error.text);
509 return -EPERM;
510 } else if (!json_is_object(data)) {
511 METRICS_LOG_WARN("JSON Request data is not a JSON object");
512 json_decref(data);
513 return -EINVAL;
514 }
515
516 ret = rte_metrics_tel_extract_data(&ep, data);
517 if (ret < 0) {
518 METRICS_LOG_ERR("Extract data function failed");
519 return ret;
520 }
521
522 ret = rte_metrics_tel_encode_json_format(&ep, &json_buffer);
523 if (ret < 0) {
524 METRICS_LOG_ERR("JSON encode function failed");
525 return ret;
526 }
527 used += strlcpy(buffer, json_buffer, buf_len);
528 return used;
529}
530
531RTE_LOG_REGISTER_DEFAULT(metrics_log_level, ERR);
532
533RTE_INIT(metrics_ctor)
534{
535#ifdef RTE_LIB_TELEMETRY
536 rte_telemetry_legacy_register("ports_all_stat_values", DATA_NOT_REQ,
537 handle_ports_all_stats_values);
538 rte_telemetry_legacy_register("global_stat_values", DATA_NOT_REQ,
539 handle_global_stats_values);
540 rte_telemetry_legacy_register("ports_stats_values_by_name", DATA_REQ,
541 handle_ports_stats_values_by_name);
542#endif
543}
544
545#else
546
547int32_t
548rte_metrics_tel_reg_all_ethdev(int *metrics_register_done, int *reg_index_list)
549{
550 RTE_SET_USED(metrics_register_done);
551 RTE_SET_USED(reg_index_list);
552
553 return -ENOTSUP;
554}
555
556int32_t
557rte_metrics_tel_encode_json_format(struct telemetry_encode_param *ep,
558 char **json_buffer)
559{
560 RTE_SET_USED(ep);
561 RTE_SET_USED(json_buffer);
562
563 return -ENOTSUP;
564}
565
566int32_t
567rte_metrics_tel_get_ports_stats_json(struct telemetry_encode_param *ep,
568 int *reg_index, char **json_buffer)
569{
570 RTE_SET_USED(ep);
571 RTE_SET_USED(reg_index);
572 RTE_SET_USED(json_buffer);
573
574 return -ENOTSUP;
575}
576
577int32_t
578rte_metrics_tel_get_port_stats_ids(struct telemetry_encode_param *ep)
579{
580 RTE_SET_USED(ep);
581
582 return -ENOTSUP;
583}
584
585int32_t
586rte_metrics_tel_extract_data(struct telemetry_encode_param *ep, json_t *data)
587{
588 RTE_SET_USED(ep);
589 RTE_SET_USED(data);
590
591 return -ENOTSUP;
592}
593
594int32_t
595rte_metrics_tel_get_global_stats(struct telemetry_encode_param *ep)
596{
597 RTE_SET_USED(ep);
598
599 return -ENOTSUP;
600}
601
602#endif
603