metrics: reduce telemetry code
[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_reg_port_ethdev_to_metrics(uint16_t port_id)
28 {
29         int ret,  num_xstats, i;
30         struct rte_eth_xstat_name *eth_xstats_names;
31         const char **xstats_names;
32
33         num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
34         if (num_xstats < 0) {
35                 METRICS_LOG_ERR("rte_eth_xstats_get(%u) failed: %d",
36                                 port_id, num_xstats);
37                 return -EPERM;
38         }
39
40         xstats_names = malloc(sizeof(*xstats_names) * num_xstats);
41         eth_xstats_names = malloc(sizeof(struct rte_eth_xstat_name)
42                         * num_xstats);
43         if (eth_xstats_names == NULL || xstats_names == NULL) {
44                 METRICS_LOG_ERR("Failed to malloc memory for xstats_names");
45                 ret = -ENOMEM;
46                 goto free_xstats;
47         }
48
49         if (rte_eth_xstats_get_names(port_id,
50                         eth_xstats_names, num_xstats) != num_xstats) {
51                 METRICS_LOG_ERR("rte_eth_xstats_get_names(%u) len %d failed",
52                                 port_id, num_xstats);
53                 ret = -EPERM;
54                 goto free_xstats;
55         }
56
57         for (i = 0; i < num_xstats; i++)
58                 xstats_names[i] = eth_xstats_names[i].name;
59         ret = rte_metrics_reg_names(xstats_names, num_xstats);
60         if (ret < 0)
61                 METRICS_LOG_ERR("rte_metrics_reg_names failed - metrics may already be registered");
62
63 free_xstats:
64         free(eth_xstats_names);
65         free(xstats_names);
66         return ret;
67 }
68
69 int32_t
70 rte_metrics_tel_reg_all_ethdev(int *metrics_register_done, int *reg_index_list)
71 {
72         struct driver_index {
73                 const void *dev_ops;
74                 int reg_index;
75         } drv_idx[RTE_MAX_ETHPORTS] = { {0} };
76         int ret, nb_drv_idx = 0;
77         uint16_t d;
78
79         RTE_ETH_FOREACH_DEV(d) {
80                 int i;
81                 /* Different device types have different numbers of stats, so
82                  * first check if the stats for this type of device have
83                  * already been registered
84                  */
85                 for (i = 0; i < nb_drv_idx; i++) {
86                         if (rte_eth_devices[d].dev_ops == drv_idx[i].dev_ops) {
87                                 reg_index_list[d] = drv_idx[i].reg_index;
88                                 break;
89                         }
90                 }
91                 if (i < nb_drv_idx)
92                         continue; /* we found a match, go to next port */
93
94                 /* No match, register a new set of xstats for this port */
95                 ret = rte_metrics_tel_reg_port_ethdev_to_metrics(d);
96                 if (ret < 0) {
97                         METRICS_LOG_ERR("Failed to register ethdev to metrics");
98                         return ret;
99                 }
100                 reg_index_list[d] = ret;
101                 drv_idx[nb_drv_idx].dev_ops = rte_eth_devices[d].dev_ops;
102                 drv_idx[nb_drv_idx].reg_index = ret;
103                 nb_drv_idx++;
104         }
105         *metrics_register_done = 1;
106         return 0;
107 }
108
109 static int32_t
110 rte_metrics_tel_update_metrics_ethdev(uint16_t port_id, int reg_start_index)
111 {
112         int ret, num_xstats, i;
113         struct rte_eth_xstat *eth_xstats;
114
115         num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
116         if (num_xstats < 0) {
117                 METRICS_LOG_ERR("rte_eth_xstats_get(%u) failed: %d", port_id,
118                                 num_xstats);
119                 return -EPERM;
120         }
121         eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats);
122         if (eth_xstats == NULL) {
123                 METRICS_LOG_ERR("Failed to malloc memory for xstats");
124                 return -ENOMEM;
125         }
126         ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats);
127         if (ret < 0 || ret > num_xstats) {
128                 free(eth_xstats);
129                 METRICS_LOG_ERR("rte_eth_xstats_get(%u) len%i failed: %d",
130                                 port_id, num_xstats, ret);
131                 return -EPERM;
132         }
133
134         uint64_t xstats_values[num_xstats];
135         for (i = 0; i < num_xstats; i++)
136                 xstats_values[i] = eth_xstats[i].value;
137         if (rte_metrics_update_values(port_id, reg_start_index, xstats_values,
138                         num_xstats) < 0) {
139                 METRICS_LOG_ERR("Could not update metrics values");
140                 free(eth_xstats);
141                 return -EPERM;
142         }
143         free(eth_xstats);
144         return 0;
145 }
146
147 static int32_t
148 rte_metrics_tel_format_port(uint32_t pid, json_t *ports,
149         uint32_t *metric_ids, int num_metric_ids)
150 {
151         struct rte_metric_value *metrics = NULL;
152         struct rte_metric_name *names = NULL;
153         int num_metrics, i, ret = -EPERM; /* most error cases return EPERM */
154         json_t *port, *stats;
155
156         num_metrics = rte_metrics_get_names(NULL, 0);
157         if (num_metrics < 0) {
158                 METRICS_LOG_ERR("Cannot get metrics count");
159                 return -EINVAL;
160         } else if (num_metrics == 0) {
161                 METRICS_LOG_ERR("No metrics to display (none have been registered)");
162                 return -EPERM;
163         }
164
165         metrics = malloc(sizeof(struct rte_metric_value) * num_metrics);
166         names = malloc(sizeof(struct rte_metric_name) * num_metrics);
167         if (metrics == NULL || names == NULL) {
168                 METRICS_LOG_ERR("Cannot allocate memory");
169                 return -ENOMEM;
170         }
171
172         if (rte_metrics_get_names(names, num_metrics) != num_metrics ||
173                         rte_metrics_get_values(pid, metrics, num_metrics)
174                                 != num_metrics) {
175                 METRICS_LOG_ERR("Error getting metrics");
176                 goto fail;
177         }
178
179         stats = json_array();
180         if (stats == NULL) {
181                 METRICS_LOG_ERR("Could not create stats JSON object");
182                 goto fail;
183         }
184
185         for (i = 0; i < num_metrics; i++) {
186                 int32_t j;
187                 for (j = 0; j < num_metric_ids; j++)
188                         if (metrics[i].key == metric_ids[j])
189                                 break;
190
191                 if (num_metric_ids > 0 && j == num_metric_ids)
192                         continue; /* can't find this id */
193
194                 json_t *stat = json_pack("{s,s,s,I}",
195                                 "name", names[metrics[i].key].name,
196                                 "value", metrics[i].value);
197                 if (stat == NULL || json_array_append_new(stats, stat) < 0) {
198                         METRICS_LOG_ERR("Format stat with id: %u failed",
199                                         metrics[i].key);
200                         goto fail;
201                 }
202         }
203
204         port = json_pack("{s,i,s,o}", "port", pid, "stats",
205                         json_array_size(stats) ? stats : json_null());
206         if (port == NULL || json_array_append_new(ports, port) < 0) {
207                 METRICS_LOG_ERR("Error creating port and adding to ports");
208                 goto fail;
209         }
210
211         free(metrics);
212         free(names);
213         return 0;
214
215 fail:
216         free(metrics);
217         free(names);
218         return ret;
219 }
220
221 int32_t
222 rte_metrics_tel_encode_json_format(struct telemetry_encode_param *ep,
223                 char **json_buffer)
224 {
225         json_t *root, *ports;
226         int ret, i;
227
228         ports = json_array();
229         if (ports == NULL) {
230                 METRICS_LOG_ERR("Could not create ports JSON array");
231                 return -EPERM;
232         }
233
234         if (ep->type == PORT_STATS) {
235                 if (ep->pp.num_port_ids <= 0) {
236                         METRICS_LOG_ERR("Please provide port/metric ids");
237                         return -EINVAL;
238                 }
239
240                 for (i = 0; i < ep->pp.num_port_ids; i++) {
241                         ret = rte_metrics_tel_format_port(ep->pp.port_ids[i],
242                                         ports, &ep->pp.metric_ids[0],
243                                         ep->pp.num_metric_ids);
244                         if (ret < 0) {
245                                 METRICS_LOG_ERR("Format port in JSON failed");
246                                 return ret;
247                         }
248                 }
249         } else if (ep->type == GLOBAL_STATS) {
250                 /* Request Global Metrics */
251                 ret = rte_metrics_tel_format_port(RTE_METRICS_GLOBAL,
252                                 ports, NULL, 0);
253                 if (ret < 0) {
254                         METRICS_LOG_ERR("Request Global Metrics Failed");
255                         return ret;
256                 }
257         } else {
258                 METRICS_LOG_ERR("Invalid metrics type in encode params");
259                 return -EINVAL;
260         }
261
262         root = json_pack("{s,s,s,o}", "status_code", "Status OK: 200",
263                         "data", ports);
264         if (root == NULL) {
265                 METRICS_LOG_ERR("Root, Status or data field cannot be set");
266                 return -EPERM;
267         }
268
269         *json_buffer = json_dumps(root, JSON_INDENT(2));
270         json_decref(root);
271         return 0;
272 }
273
274 int32_t
275 rte_metrics_tel_get_ports_stats_json(struct telemetry_encode_param *ep,
276                 int *reg_index, char **json_buffer)
277 {
278         int ret, i;
279         uint32_t port_id;
280
281         for (i = 0; i < ep->pp.num_port_ids; i++) {
282                 port_id = ep->pp.port_ids[i];
283                 if (!rte_eth_dev_is_valid_port(port_id)) {
284                         METRICS_LOG_ERR("Port: %d invalid", port_id);
285                         return -EINVAL;
286                 }
287
288                 ret = rte_metrics_tel_update_metrics_ethdev(port_id,
289                                 reg_index[i]);
290                 if (ret < 0) {
291                         METRICS_LOG_ERR("Failed to update ethdev metrics");
292                         return ret;
293                 }
294         }
295
296         ret = rte_metrics_tel_encode_json_format(ep, json_buffer);
297         if (ret < 0) {
298                 METRICS_LOG_ERR("JSON encode function failed");
299                 return ret;
300         }
301         return 0;
302 }
303
304 int32_t
305 rte_metrics_tel_get_port_stats_ids(struct telemetry_encode_param *ep)
306 {
307         int p, num_port_ids = 0;
308
309         RTE_ETH_FOREACH_DEV(p) {
310                 ep->pp.port_ids[num_port_ids] = p;
311                 num_port_ids++;
312         }
313
314         if (!num_port_ids) {
315                 METRICS_LOG_ERR("No active ports");
316                 return -EINVAL;
317         }
318
319         ep->pp.num_port_ids = num_port_ids;
320         ep->pp.num_metric_ids = 0;
321         ep->type = PORT_STATS;
322         return 0;
323 }
324
325 static int32_t
326 rte_metrics_tel_stat_names_to_ids(const char * const *stat_names,
327         uint32_t *stat_ids, int num_stat_names)
328 {
329         struct rte_metric_name *names;
330         int num_metrics;
331         int i, j, nb_stat_ids = 0;
332
333         num_metrics = rte_metrics_get_names(NULL, 0);
334         if (num_metrics <= 0) {
335                 METRICS_LOG_ERR("Error getting metrics count - no metrics may be registered");
336                 return -EPERM;
337         }
338
339         names = malloc(sizeof(struct rte_metric_name) * num_metrics);
340         if (names == NULL) {
341                 METRICS_LOG_ERR("Cannot allocate memory for names");
342                 return -ENOMEM;
343         }
344
345         if (rte_metrics_get_names(names, num_metrics) != num_metrics) {
346                 METRICS_LOG_ERR("Cannot get metrics names");
347                 free(names);
348                 return -EPERM;
349         }
350
351         for (i = 0; i < num_stat_names; i++) {
352                 for (j = 0; j < num_metrics; j++) {
353                         if (strcmp(stat_names[i], names[j].name) == 0) {
354                                 stat_ids[nb_stat_ids++] = j;
355                                 break;
356                         }
357                 }
358                 if (j == num_metrics) {
359                         METRICS_LOG_WARN("Invalid stat name %s\n",
360                                         stat_names[i]);
361                         free(names);
362                         return -EINVAL;
363                 }
364         }
365
366         free(names);
367         return 0;
368 }
369
370 int32_t
371 rte_metrics_tel_extract_data(struct telemetry_encode_param *ep, json_t *data)
372 {
373         int ret;
374         json_t *port_ids_json = json_object_get(data, "ports");
375         json_t *stat_names_json = json_object_get(data, "stats");
376         uint64_t num_stat_names = json_array_size(stat_names_json);
377         const char *stat_names[num_stat_names];
378         size_t index;
379         json_t *value;
380
381         memset(ep, 0, sizeof(*ep));
382         ep->pp.num_port_ids = json_array_size(port_ids_json);
383         ep->pp.num_metric_ids = num_stat_names;
384         if (!json_is_object(data) || !json_is_array(port_ids_json) ||
385                         !json_is_array(stat_names_json)) {
386                 METRICS_LOG_WARN("Invalid data provided for this command");
387                 return -EINVAL;
388         }
389
390         json_array_foreach(port_ids_json, index, value) {
391                 if (!json_is_integer(value)) {
392                         METRICS_LOG_WARN("Port ID given is not valid");
393                         return -EINVAL;
394                 }
395                 ep->pp.port_ids[index] = json_integer_value(value);
396                 if (rte_eth_dev_is_valid_port(ep->pp.port_ids[index]) < 1)
397                         return -EINVAL;
398         }
399         json_array_foreach(stat_names_json, index, value) {
400                 if (!json_is_string(value)) {
401                         METRICS_LOG_WARN("Stat Name given is not a string");
402                         return -EINVAL;
403                 }
404                 stat_names[index] = json_string_value(value);
405         }
406
407         ret = rte_metrics_tel_stat_names_to_ids(stat_names, ep->pp.metric_ids,
408                         num_stat_names);
409         if (ret < 0) {
410                 METRICS_LOG_ERR("Could not convert stat names to IDs");
411                 return ret;
412         }
413
414         ep->type = PORT_STATS;
415         return 0;
416 }
417
418 RTE_INIT(metrics_ctor)
419 {
420         metrics_log_level = rte_log_register("lib.metrics");
421         if (metrics_log_level >= 0)
422                 rte_log_set_level(metrics_log_level, RTE_LOG_ERR);
423 }