ethdev: hide internal structures
[dpdk.git] / lib / metrics / rte_metrics_telemetry.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2020 Intel Corporation
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
16 struct telemetry_metrics_data tel_met_data;
17
18 int metrics_log_level;
19
20 /* Logging Macros */
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
31 static int32_t
32 rte_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
68 free_xstats:
69         free(eth_xstats_names);
70         free(xstats_names);
71         return ret;
72 }
73
74 int32_t
75 rte_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                 /* Different device types have different numbers of stats, so
88                  * first check if the stats for this type of device have
89                  * already been registered
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; /* we found a match, go to next port */
99
100                 /* No match, register a new set of xstats for this port */
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
115 static int32_t
116 rte_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
153 static int32_t
154 rte_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; /* most error cases return 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; /* can't find this id */
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
222 fail:
223         free(metrics);
224         free(names);
225         return ret;
226 }
227
228 int32_t
229 rte_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                 /* Request Global Metrics */
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
281 int32_t
282 rte_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
311 int32_t
312 rte_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
332 static int32_t
333 rte_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
377 int32_t
378 rte_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
425 static int
426 rte_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
441 static int
442 handle_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
468 static int
469 handle_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
490 static int
491 handle_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
531 RTE_LOG_REGISTER_DEFAULT(metrics_log_level, ERR);
532
533 RTE_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 /* !RTE_HAS_JANSSON */
546
547 int32_t
548 rte_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
556 int32_t
557 rte_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
566 int32_t
567 rte_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
577 int32_t
578 rte_metrics_tel_get_port_stats_ids(struct telemetry_encode_param *ep)
579 {
580         RTE_SET_USED(ep);
581
582         return -ENOTSUP;
583 }
584
585 int32_t
586 rte_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
594 int32_t
595 rte_metrics_tel_get_global_stats(struct telemetry_encode_param *ep)
596 {
597         RTE_SET_USED(ep);
598
599         return -ENOTSUP;
600 }
601
602 #endif /* !RTE_HAS_JANSSON */