telemetry: move some functions to metrics library
[dpdk.git] / lib / librte_metrics / rte_metrics_telemetry.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2020 Intel Corporation
3  */
4
5 #include <jansson.h>
6
7 #include <rte_ethdev.h>
8 #include <rte_string_fns.h>
9
10 #include "rte_metrics.h"
11 #include "rte_metrics_telemetry.h"
12
13 int metrics_log_level;
14
15 /* Logging Macros */
16 #define METRICS_LOG(level, fmt, args...) \
17         rte_log(RTE_LOG_ ##level, metrics_log_level, "%s(): "fmt "\n", \
18                 __func__, ##args)
19
20 #define METRICS_LOG_ERR(fmt, args...) \
21         METRICS_LOG(ERR, fmt, ## args)
22
23 #define METRICS_LOG_WARN(fmt, args...) \
24         METRICS_LOG(WARNING, fmt, ## args)
25
26 static int32_t
27 rte_metrics_tel_is_port_active(int port_id)
28 {
29         int ret;
30
31         ret = rte_eth_find_next(port_id);
32         if (ret == port_id)
33                 return 1;
34
35         METRICS_LOG_ERR("port_id: %d is invalid, not active",
36                 port_id);
37
38         return 0;
39 }
40
41 static int32_t
42 rte_metrics_tel_reg_port_ethdev_to_metrics(uint16_t port_id)
43 {
44         int ret, num_xstats, ret_val, i;
45         struct rte_eth_xstat *eth_xstats = NULL;
46         struct rte_eth_xstat_name *eth_xstats_names = NULL;
47
48         if (!rte_eth_dev_is_valid_port(port_id)) {
49                 METRICS_LOG_ERR("port_id: %d is invalid", port_id);
50                 return -EINVAL;
51         }
52
53         num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
54         if (num_xstats < 0) {
55                 METRICS_LOG_ERR("rte_eth_xstats_get(%u) failed: %d",
56                                 port_id, num_xstats);
57                 return -EPERM;
58         }
59
60         eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats);
61         if (eth_xstats == NULL) {
62                 METRICS_LOG_ERR("Failed to malloc memory for xstats");
63                 return -ENOMEM;
64         }
65
66         ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats);
67         const char *xstats_names[num_xstats];
68         eth_xstats_names = malloc(sizeof(struct rte_eth_xstat_name)
69                         * num_xstats);
70         if (ret < 0 || ret > num_xstats) {
71                 METRICS_LOG_ERR("rte_eth_xstats_get(%u) len%i failed: %d",
72                                 port_id, num_xstats, ret);
73                 ret_val = -EPERM;
74                 goto free_xstats;
75         }
76
77         if (eth_xstats_names == NULL) {
78                 METRICS_LOG_ERR("Failed to malloc memory for xstats_names");
79                 ret_val = -ENOMEM;
80                 goto free_xstats;
81         }
82
83         ret = rte_eth_xstats_get_names(port_id, eth_xstats_names, num_xstats);
84         if (ret < 0 || ret > num_xstats) {
85                 METRICS_LOG_ERR("rte_eth_xstats_get_names(%u) len%i failed: %d",
86                                 port_id, num_xstats, ret);
87                 ret_val = -EPERM;
88                 goto free_xstats;
89         }
90
91         for (i = 0; i < num_xstats; i++)
92                 xstats_names[i] = eth_xstats_names[eth_xstats[i].id].name;
93
94         ret_val = rte_metrics_reg_names(xstats_names, num_xstats);
95         if (ret_val < 0) {
96                 METRICS_LOG_ERR("rte_metrics_reg_names failed - metrics may already be registered");
97                 ret_val = -1;
98                 goto free_xstats;
99         }
100
101         goto free_xstats;
102
103 free_xstats:
104         free(eth_xstats);
105         free(eth_xstats_names);
106         return ret_val;
107 }
108
109 int32_t
110 rte_metrics_tel_reg_all_ethdev(int *metrics_register_done, int *reg_index_list)
111 {
112         struct driver_index {
113                 const void *dev_ops;
114                 int reg_index;
115         } drv_idx[RTE_MAX_ETHPORTS] = { {0} };
116         int nb_drv_idx = 0;
117         uint16_t pid;
118         int ret;
119
120         RTE_ETH_FOREACH_DEV(pid) {
121                 int i;
122                 /* Different device types have different numbers of stats, so
123                  * first check if the stats for this type of device have
124                  * already been registered
125                  */
126                 for (i = 0; i < nb_drv_idx; i++) {
127                         if (rte_eth_devices[pid].dev_ops ==
128                                         drv_idx[i].dev_ops) {
129                                 reg_index_list[pid] = drv_idx[i].reg_index;
130                                 break;
131                         }
132                 }
133                 if (i < nb_drv_idx)
134                         continue; /* we found a match, go to next port */
135
136                 /* No match, register a new set of xstats for this port */
137                 ret = rte_metrics_tel_reg_port_ethdev_to_metrics(pid);
138                 if (ret < 0) {
139                         METRICS_LOG_ERR("Failed to register ethdev metrics");
140                         return -1;
141                 }
142                 reg_index_list[pid] = ret;
143                 drv_idx[nb_drv_idx].dev_ops = rte_eth_devices[pid].dev_ops;
144                 drv_idx[nb_drv_idx].reg_index = ret;
145                 nb_drv_idx++;
146         }
147
148         *metrics_register_done = 1;
149         return 0;
150 }
151
152 static int32_t
153 rte_metrics_tel_update_metrics_ethdev(uint16_t port_id, int reg_start_index)
154 {
155         int ret, num_xstats, i;
156         struct rte_eth_xstat *eth_xstats;
157
158         if (!rte_eth_dev_is_valid_port(port_id)) {
159                 METRICS_LOG_ERR("port_id: %d is invalid", port_id);
160                 return -EINVAL;
161         }
162
163         ret = rte_metrics_tel_is_port_active(port_id);
164         if (ret < 1)
165                 return -EINVAL;
166
167         num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
168         if (num_xstats < 0) {
169                 METRICS_LOG_ERR("rte_eth_xstats_get(%u) failed: %d", port_id,
170                                 num_xstats);
171                 return -EPERM;
172         }
173
174         eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats);
175         if (eth_xstats == NULL) {
176                 METRICS_LOG_ERR("Failed to malloc memory for xstats");
177                 return -ENOMEM;
178         }
179
180         ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats);
181         if (ret < 0 || ret > num_xstats) {
182                 free(eth_xstats);
183                 METRICS_LOG_ERR("rte_eth_xstats_get(%u) len%i failed: %d",
184                                 port_id, num_xstats, ret);
185                 return -EPERM;
186         }
187
188         uint64_t xstats_values[num_xstats];
189         for (i = 0; i < num_xstats; i++)
190                 xstats_values[i] = eth_xstats[i].value;
191
192         ret = rte_metrics_update_values(port_id, reg_start_index, xstats_values,
193                         num_xstats);
194         if (ret < 0) {
195                 METRICS_LOG_ERR("Could not update metrics values");
196                 free(eth_xstats);
197                 return -EPERM;
198         }
199
200         free(eth_xstats);
201         return 0;
202 }
203
204 static int
205 rte_metrics_tel_get_metrics(uint32_t port_id, struct rte_metric_value
206         *metrics, struct rte_metric_name *names, int num_metrics)
207 {
208         int ret, num_values;
209
210         if (num_metrics < 0) {
211                 METRICS_LOG_ERR("Invalid metrics count");
212                 return -EINVAL;
213         } else if (num_metrics == 0) {
214                 METRICS_LOG_ERR("No metrics to display (none have been registered)");
215                 return -EPERM;
216         }
217
218         if (metrics == NULL) {
219                 METRICS_LOG_ERR("Metrics must be initialised.");
220                 return -EINVAL;
221         }
222
223         if (names == NULL) {
224                 METRICS_LOG_ERR("Names must be initialised.");
225                 return -EINVAL;
226         }
227
228         ret = rte_metrics_get_names(names, num_metrics);
229         if (ret < 0 || ret > num_metrics) {
230                 METRICS_LOG_ERR("Cannot get metrics names");
231                 return -EPERM;
232         }
233
234         num_values = rte_metrics_get_values(port_id, NULL, 0);
235         ret = rte_metrics_get_values(port_id, metrics, num_values);
236         if (ret < 0 || ret > num_values) {
237                 METRICS_LOG_ERR("Cannot get metrics values");
238                 return -EPERM;
239         }
240
241         return 0;
242 }
243
244 static int32_t
245 rte_metrics_tel_json_format_stat(json_t *stats, const char *metric_name,
246         uint64_t metric_value)
247 {
248         int ret;
249         json_t *stat = json_object();
250
251         if (stat == NULL) {
252                 METRICS_LOG_ERR("Could not create stat JSON object");
253                 return -EPERM;
254         }
255
256         ret = json_object_set_new(stat, "name", json_string(metric_name));
257         if (ret < 0) {
258                 METRICS_LOG_ERR("Stat Name field cannot be set");
259                 return -EPERM;
260         }
261
262         ret = json_object_set_new(stat, "value", json_integer(metric_value));
263         if (ret < 0) {
264                 METRICS_LOG_ERR("Stat Value field cannot be set");
265                 return -EPERM;
266         }
267
268         ret = json_array_append_new(stats, stat);
269         if (ret < 0) {
270                 METRICS_LOG_ERR("Stat cannot be added to stats json array");
271                 return -EPERM;
272         }
273
274         return 0;
275 }
276
277 static int32_t
278 rte_metrics_tel_json_format_port(uint32_t port_id, json_t *ports,
279         uint32_t *metric_ids, int num_metric_ids)
280 {
281         struct rte_metric_value *metrics = 0;
282         struct rte_metric_name *names = 0;
283         int num_metrics, ret;
284         json_t *port, *stats;
285         int i;
286
287         num_metrics = rte_metrics_get_names(NULL, 0);
288         if (num_metrics < 0) {
289                 METRICS_LOG_ERR("Cannot get metrics count");
290                 goto einval_fail;
291         } else if (num_metrics == 0) {
292                 METRICS_LOG_ERR("No metrics to display (none have been registered)");
293                 goto eperm_fail;
294         }
295
296         metrics = malloc(sizeof(struct rte_metric_value) * num_metrics);
297         names = malloc(sizeof(struct rte_metric_name) * num_metrics);
298         if (metrics == NULL || names == NULL) {
299                 METRICS_LOG_ERR("Cannot allocate memory");
300                 free(metrics);
301                 free(names);
302                 return -ENOMEM;
303         }
304
305         ret  = rte_metrics_tel_get_metrics(port_id, metrics, names,
306                         num_metrics);
307         if (ret < 0) {
308                 free(metrics);
309                 free(names);
310                 METRICS_LOG_ERR("rte_metrics_tel_get_metrics failed");
311                 return ret;
312         }
313
314         port = json_object();
315         stats = json_array();
316         if (port == NULL || stats == NULL) {
317                 METRICS_LOG_ERR("Could not create port/stats JSON objects");
318                 goto eperm_fail;
319         }
320
321         ret = json_object_set_new(port, "port", json_integer(port_id));
322         if (ret < 0) {
323                 METRICS_LOG_ERR("Port field cannot be set");
324                 goto eperm_fail;
325         }
326
327         for (i = 0; i < num_metric_ids; i++) {
328                 int metric_id = metric_ids[i];
329                 int metric_index = -1;
330                 int metric_name_key = -1;
331                 int32_t j;
332                 uint64_t metric_value;
333
334                 if (metric_id >= num_metrics) {
335                         METRICS_LOG_ERR("Metric_id: %d is not valid",
336                                         metric_id);
337                         goto einval_fail;
338                 }
339
340                 for (j = 0; j < num_metrics; j++) {
341                         if (metrics[j].key == metric_id) {
342                                 metric_name_key = metrics[j].key;
343                                 metric_index = j;
344                                 break;
345                         }
346                 }
347
348                 const char *metric_name = names[metric_name_key].name;
349                 metric_value = metrics[metric_index].value;
350
351                 if (metric_name_key < 0 || metric_index < 0) {
352                         METRICS_LOG_ERR("Could not get metric name/index");
353                         goto eperm_fail;
354                 }
355
356                 ret = rte_metrics_tel_json_format_stat(stats, metric_name,
357                                 metric_value);
358                 if (ret < 0) {
359                         METRICS_LOG_ERR("Format stat with id: %u failed",
360                                         metric_id);
361                         free(metrics);
362                         free(names);
363                         return ret;
364                 }
365         }
366
367         if (json_array_size(stats) == 0)
368                 ret = json_object_set_new(port, "stats", json_null());
369         else
370                 ret = json_object_set_new(port, "stats", stats);
371
372         if (ret < 0) {
373                 METRICS_LOG_ERR("Stats object cannot be set");
374                 goto eperm_fail;
375         }
376
377         ret = json_array_append_new(ports, port);
378         if (ret < 0) {
379                 METRICS_LOG_ERR("Port object cannot be added to ports array");
380                 goto eperm_fail;
381         }
382
383         free(metrics);
384         free(names);
385         return 0;
386
387 eperm_fail:
388         free(metrics);
389         free(names);
390         return -EPERM;
391
392 einval_fail:
393         free(metrics);
394         free(names);
395         return -EINVAL;
396 }
397
398 int32_t
399 rte_metrics_tel_encode_json_format(struct telemetry_encode_param *ep,
400                 char **json_buffer)
401 {
402         int ret;
403         json_t *root, *ports;
404         int i;
405         uint32_t port_id;
406         int num_port_ids;
407         int num_metric_ids;
408
409         ports = json_array();
410         if (ports == NULL) {
411                 METRICS_LOG_ERR("Could not create ports JSON array");
412                 return -EPERM;
413         }
414
415         if (ep->type == PORT_STATS) {
416                 num_port_ids = ep->pp.num_port_ids;
417                 num_metric_ids = ep->pp.num_metric_ids;
418
419                 if (num_port_ids <= 0 || num_metric_ids <= 0) {
420                         METRICS_LOG_ERR("Please provide port and metric ids to query");
421                         return -EINVAL;
422                 }
423
424                 for (i = 0; i < num_port_ids; i++) {
425                         port_id = ep->pp.port_ids[i];
426                         if (!rte_eth_dev_is_valid_port(port_id)) {
427                                 METRICS_LOG_ERR("Port: %d invalid",
428                                                 port_id);
429                                 return -EINVAL;
430                         }
431                 }
432
433                 for (i = 0; i < num_port_ids; i++) {
434                         port_id = ep->pp.port_ids[i];
435                         ret = rte_metrics_tel_json_format_port(port_id,
436                                         ports, &ep->pp.metric_ids[0],
437                                         num_metric_ids);
438                         if (ret < 0) {
439                                 METRICS_LOG_ERR("Format port in JSON failed");
440                                 return ret;
441                         }
442                 }
443         } else if (ep->type == GLOBAL_STATS) {
444                 /* Request Global Metrics */
445                 ret = rte_metrics_tel_json_format_port(RTE_METRICS_GLOBAL,
446                                 ports, &ep->gp.metric_ids[0],
447                                 ep->gp.num_metric_ids);
448                 if (ret < 0) {
449                         METRICS_LOG_ERR(" Request Global Metrics Failed");
450                         return ret;
451                 }
452         } else {
453                 METRICS_LOG_ERR(" Invalid metrics type in encode params");
454                 return -EINVAL;
455         }
456
457         root = json_object();
458         if (root == NULL) {
459                 METRICS_LOG_ERR("Could not create root JSON object");
460                 return -EPERM;
461         }
462
463         ret = json_object_set_new(root, "status_code",
464                 json_string("Status OK: 200"));
465         if (ret < 0) {
466                 METRICS_LOG_ERR("Status code field cannot be set");
467                 return -EPERM;
468         }
469
470         ret = json_object_set_new(root, "data", ports);
471         if (ret < 0) {
472                 METRICS_LOG_ERR("Data field cannot be set");
473                 return -EPERM;
474         }
475
476         *json_buffer = json_dumps(root, JSON_INDENT(2));
477         json_decref(root);
478         return 0;
479 }
480
481 int32_t
482 rte_metrics_tel_get_global_stats(struct telemetry_encode_param *ep)
483 {
484         int num_metrics, ret, i;
485         struct rte_metric_value *values;
486
487         num_metrics = rte_metrics_get_values(RTE_METRICS_GLOBAL, NULL, 0);
488         if (num_metrics < 0) {
489                 METRICS_LOG_ERR("Cannot get metrics count");
490                 return -EINVAL;
491         } else if (num_metrics == 0) {
492                 METRICS_LOG_ERR("No metrics to display (none have been registered)");
493                 return -EPERM;
494         }
495
496         values = malloc(sizeof(struct rte_metric_value) * num_metrics);
497         if (values == NULL) {
498                 METRICS_LOG_ERR("Cannot allocate memory");
499                 return -ENOMEM;
500         }
501
502         ret = rte_metrics_get_values(RTE_METRICS_GLOBAL, values, num_metrics);
503         if (ret < 0) {
504                 METRICS_LOG_ERR("Could not get stat values");
505                 free(values);
506                 return -EINVAL;
507         }
508         for (i = 0; i < num_metrics; i++)
509                 ep->gp.metric_ids[i] = values[i].key;
510
511         ep->gp.num_metric_ids = num_metrics;
512         ep->type = GLOBAL_STATS;
513         free(values);
514         return 0;
515 }
516
517 int32_t
518 rte_metrics_tel_get_ports_stats_json(struct telemetry_encode_param *ep,
519                 int *reg_index, char **json_buffer)
520 {
521         int ret, i;
522         uint32_t port_id;
523
524         for (i = 0; i < ep->pp.num_port_ids; i++) {
525                 port_id = ep->pp.port_ids[i];
526                 if (!rte_eth_dev_is_valid_port(port_id)) {
527                         METRICS_LOG_ERR("Port: %d invalid", port_id);
528                         return -EINVAL;
529                 }
530
531                 ret = rte_metrics_tel_update_metrics_ethdev(port_id,
532                                 reg_index[i]);
533                 if (ret < 0) {
534                         METRICS_LOG_ERR("Failed to update ethdev metrics");
535                         return ret;
536                 }
537         }
538
539         ret = rte_metrics_tel_encode_json_format(ep, json_buffer);
540         if (ret < 0) {
541                 METRICS_LOG_ERR("JSON encode function failed");
542                 return ret;
543         }
544         return 0;
545 }
546
547 int32_t
548 rte_metrics_tel_get_port_stats_ids(struct telemetry_encode_param *ep)
549 {
550         int ret, num_metrics, i, p;
551         struct rte_metric_value *values;
552         uint64_t num_port_ids = 0;
553
554         num_metrics = rte_metrics_get_values(0, NULL, 0);
555         if (num_metrics < 0) {
556                 METRICS_LOG_ERR("Cannot get metrics count");
557                 return -EINVAL;
558         } else if (num_metrics == 0) {
559                 METRICS_LOG_ERR("No metrics to display (none have been registered)");
560                 return -EPERM;
561         }
562
563         values = malloc(sizeof(struct rte_metric_value) * num_metrics);
564         if (values == NULL) {
565                 METRICS_LOG_ERR("Cannot allocate memory");
566                 return -ENOMEM;
567         }
568
569         RTE_ETH_FOREACH_DEV(p) {
570                 ep->pp.port_ids[num_port_ids] = p;
571                 num_port_ids++;
572         }
573
574         if (!num_port_ids) {
575                 METRICS_LOG_ERR("No active ports");
576                 goto fail;
577         }
578
579         ret = rte_metrics_get_values(ep->pp.port_ids[0], values, num_metrics);
580         if (ret < 0) {
581                 METRICS_LOG_ERR("Could not get stat values");
582                 goto fail;
583         }
584         for (i = 0; i < num_metrics; i++)
585                 ep->pp.metric_ids[i] = values[i].key;
586
587         ep->pp.num_port_ids = num_port_ids;
588         ep->pp.num_metric_ids = num_metrics;
589         ep->type = PORT_STATS;
590         return 0;
591
592 fail:
593         free(values);
594         return -EINVAL;
595 }
596
597 static int32_t
598 rte_metrics_tel_stat_names_to_ids(const char * const *stat_names,
599         uint32_t *stat_ids, uint64_t num_stat_names)
600 {
601         struct rte_metric_name *names;
602         int ret, num_metrics;
603         uint32_t i, k;
604
605         if (stat_names == NULL) {
606                 METRICS_LOG_WARN("Invalid stat_names argument");
607                 return -EINVAL;
608         }
609
610         if (num_stat_names <= 0) {
611                 METRICS_LOG_WARN("Invalid num_stat_names argument");
612                 return -EINVAL;
613         }
614
615         num_metrics = rte_metrics_get_names(NULL, 0);
616         if (num_metrics < 0) {
617                 METRICS_LOG_ERR("Cannot get metrics count");
618                 return -EPERM;
619         } else if (num_metrics == 0) {
620                 METRICS_LOG_WARN("No metrics have been registered");
621                 return -EPERM;
622         }
623
624         names = malloc(sizeof(struct rte_metric_name) * num_metrics);
625         if (names == NULL) {
626                 METRICS_LOG_ERR("Cannot allocate memory for names");
627                 return -ENOMEM;
628         }
629
630         ret = rte_metrics_get_names(names, num_metrics);
631         if (ret < 0 || ret > num_metrics) {
632                 METRICS_LOG_ERR("Cannot get metrics names");
633                 free(names);
634                 return -EPERM;
635         }
636
637         k = 0;
638         for (i = 0; i < (uint32_t)num_stat_names; i++) {
639                 uint32_t j;
640                 for (j = 0; j < (uint32_t)num_metrics; j++) {
641                         if (strcmp(stat_names[i], names[j].name) == 0) {
642                                 stat_ids[k] = j;
643                                 k++;
644                                 break;
645                         }
646                 }
647         }
648
649         if (k != num_stat_names) {
650                 METRICS_LOG_WARN("Invalid stat names provided");
651                 free(names);
652                 return -EINVAL;
653         }
654
655         free(names);
656         return 0;
657 }
658
659 int32_t
660 rte_metrics_tel_extract_data(struct telemetry_encode_param *ep, json_t *data)
661 {
662         int ret;
663         json_t *port_ids_json = json_object_get(data, "ports");
664         json_t *stat_names_json = json_object_get(data, "stats");
665         uint64_t num_stat_names = json_array_size(stat_names_json);
666         const char *stat_names[num_stat_names];
667         size_t index;
668         json_t *value;
669
670         memset(ep, 0, sizeof(*ep));
671         ep->pp.num_port_ids = json_array_size(port_ids_json);
672         ep->pp.num_metric_ids = num_stat_names;
673         if (!json_is_object(data)) {
674                 METRICS_LOG_WARN("Invalid data provided for this command");
675                 return -EINVAL;
676         }
677
678         if (!json_is_array(port_ids_json) ||
679                  !json_is_array(stat_names_json)) {
680                 METRICS_LOG_WARN("Invalid input data array(s)");
681                 return -EINVAL;
682         }
683
684         json_array_foreach(port_ids_json, index, value) {
685                 if (!json_is_integer(value)) {
686                         METRICS_LOG_WARN("Port ID given is not valid");
687                         return -EINVAL;
688                 }
689                 ep->pp.port_ids[index] = json_integer_value(value);
690                 ret = rte_metrics_tel_is_port_active(ep->pp.port_ids[index]);
691                 if (ret < 1)
692                         return -EINVAL;
693         }
694
695         json_array_foreach(stat_names_json, index, value) {
696                 if (!json_is_string(value)) {
697                         METRICS_LOG_WARN("Stat Name given is not a string");
698                         return -EINVAL;
699                 }
700                 stat_names[index] = json_string_value(value);
701         }
702
703         ret = rte_metrics_tel_stat_names_to_ids(stat_names, ep->pp.metric_ids,
704                         num_stat_names);
705         if (ret < 0) {
706                 METRICS_LOG_ERR("Could not convert stat names to IDs");
707                 return ret;
708         }
709
710         ep->type = PORT_STATS;
711         return 0;
712 }
713
714 RTE_INIT(metrics_ctor)
715 {
716         metrics_log_level = rte_log_register("lib.metrics");
717         if (metrics_log_level >= 0)
718                 rte_log_set_level(metrics_log_level, RTE_LOG_ERR);
719 }