telemetry: add initial connection socket
[dpdk.git] / lib / librte_telemetry / rte_telemetry.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2018 Intel Corporation
3  */
4
5 #include <unistd.h>
6 #include <fcntl.h>
7 #include <pthread.h>
8 #include <sys/socket.h>
9 #include <sys/un.h>
10
11 #include <rte_eal.h>
12 #include <rte_ethdev.h>
13 #include <rte_metrics.h>
14 #include <rte_option.h>
15 #include <rte_string_fns.h>
16
17 #include "rte_telemetry.h"
18 #include "rte_telemetry_internal.h"
19
20 #define BUF_SIZE 1024
21 #define SLEEP_TIME 10
22
23 static telemetry_impl *static_telemetry;
24
25 static void
26 rte_telemetry_get_runtime_dir(char *socket_path, size_t size)
27 {
28         snprintf(socket_path, size, "%s/telemetry", rte_eal_get_runtime_dir());
29 }
30
31 int32_t
32 rte_telemetry_is_port_active(int port_id)
33 {
34         int ret;
35
36         ret = rte_eth_find_next(port_id);
37         if (ret == port_id)
38                 return 1;
39
40         TELEMETRY_LOG_ERR("port_id: %d is invalid, not active",
41                 port_id);
42         return 0;
43 }
44
45 static int32_t
46 rte_telemetry_reg_ethdev_to_metrics(uint16_t port_id)
47 {
48         int ret, num_xstats, ret_val, i;
49         struct rte_eth_xstat *eth_xstats = NULL;
50         struct rte_eth_xstat_name *eth_xstats_names = NULL;
51
52         if (!rte_eth_dev_is_valid_port(port_id)) {
53                 TELEMETRY_LOG_ERR("port_id: %d is invalid", port_id);
54                 return -EINVAL;
55         }
56
57         num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
58         if (num_xstats < 0) {
59                 TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) failed: %d",
60                                 port_id, num_xstats);
61                 return -EPERM;
62         }
63
64         eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats);
65         if (eth_xstats == NULL) {
66                 TELEMETRY_LOG_ERR("Failed to malloc memory for xstats");
67                 return -ENOMEM;
68         }
69
70         ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats);
71         const char *xstats_names[num_xstats];
72         eth_xstats_names = malloc(sizeof(struct rte_eth_xstat_name) * num_xstats);
73         if (ret < 0 || ret > num_xstats) {
74                 TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) len%i failed: %d",
75                                 port_id, num_xstats, ret);
76                 ret_val = -EPERM;
77                 goto free_xstats;
78         }
79
80         if (eth_xstats_names == NULL) {
81                 TELEMETRY_LOG_ERR("Failed to malloc memory for xstats_names");
82                 ret_val = -ENOMEM;
83                 goto free_xstats;
84         }
85
86         ret = rte_eth_xstats_get_names(port_id, eth_xstats_names, num_xstats);
87         if (ret < 0 || ret > num_xstats) {
88                 TELEMETRY_LOG_ERR("rte_eth_xstats_get_names(%u) len%i failed: %d",
89                                 port_id, num_xstats, ret);
90                 ret_val = -EPERM;
91                 goto free_xstats;
92         }
93
94         for (i = 0; i < num_xstats; i++)
95                 xstats_names[i] = eth_xstats_names[eth_xstats[i].id].name;
96
97         ret_val = rte_metrics_reg_names(xstats_names, num_xstats);
98         if (ret_val < 0) {
99                 TELEMETRY_LOG_ERR("rte_metrics_reg_names failed - metrics may already be registered");
100                 ret_val = -1;
101                 goto free_xstats;
102         }
103
104         goto free_xstats;
105
106 free_xstats:
107         free(eth_xstats);
108         free(eth_xstats_names);
109         return ret_val;
110 }
111
112 static int32_t
113 rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
114 {
115         uint16_t pid;
116
117         RTE_ETH_FOREACH_DEV(pid) {
118                 telemetry->reg_index =
119                         rte_telemetry_reg_ethdev_to_metrics(pid);
120                 break;
121         }
122
123         if (telemetry->reg_index < 0) {
124                 TELEMETRY_LOG_ERR("Failed to register ethdev metrics");
125                 return -1;
126         }
127
128         telemetry->metrics_register_done = 1;
129
130         return 0;
131 }
132
133 static int32_t
134 rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
135 {
136         int ret;
137
138         if (telemetry->accept_fd <= 0) {
139                 ret = listen(telemetry->server_fd, 1);
140                 if (ret < 0) {
141                         TELEMETRY_LOG_ERR("Listening error with server fd");
142                         return -1;
143                 }
144                 telemetry->accept_fd = accept(telemetry->server_fd, NULL, NULL);
145
146                 if (telemetry->accept_fd >= 0 &&
147                         telemetry->metrics_register_done == 0) {
148                         ret = rte_telemetry_initial_accept(telemetry);
149                         if (ret < 0) {
150                                 TELEMETRY_LOG_ERR("Failed to run initial configurations/tests");
151                                 return -1;
152                         }
153                 }
154         }
155
156         return 0;
157 }
158
159 static int32_t
160 rte_telemetry_run(void *userdata)
161 {
162         int ret;
163         struct telemetry_impl *telemetry = userdata;
164
165         if (telemetry == NULL) {
166                 TELEMETRY_LOG_WARN("TELEMETRY could not be initialised");
167                 return -1;
168         }
169
170         ret = rte_telemetry_accept_new_client(telemetry);
171         if (ret < 0) {
172                 TELEMETRY_LOG_ERR("Accept and read new client failed");
173                 return -1;
174         }
175
176         return 0;
177 }
178
179 static void
180 *rte_telemetry_run_thread_func(void *userdata)
181 {
182         int ret;
183         struct telemetry_impl *telemetry = userdata;
184
185         if (telemetry == NULL) {
186                 TELEMETRY_LOG_ERR("%s passed a NULL instance", __func__);
187                 pthread_exit(0);
188         }
189
190         while (telemetry->thread_status) {
191                 rte_telemetry_run(telemetry);
192                 ret = usleep(SLEEP_TIME);
193                 if (ret < 0)
194                         TELEMETRY_LOG_ERR("Calling thread could not be put to sleep");
195         }
196         pthread_exit(0);
197 }
198
199 static int32_t
200 rte_telemetry_set_socket_nonblock(int fd)
201 {
202         int flags;
203
204         if (fd < 0) {
205                 TELEMETRY_LOG_ERR("Invalid fd provided");
206                 return -1;
207         }
208
209         flags = fcntl(fd, F_GETFL, 0);
210         if (flags < 0)
211                 flags = 0;
212
213         return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
214 }
215
216 static int32_t
217 rte_telemetry_create_socket(struct telemetry_impl *telemetry)
218 {
219         int ret;
220         struct sockaddr_un addr;
221         char socket_path[BUF_SIZE];
222
223         if (telemetry == NULL)
224                 return -1;
225
226         telemetry->server_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
227         if (telemetry->server_fd == -1) {
228                 TELEMETRY_LOG_ERR("Failed to open socket");
229                 return -1;
230         }
231
232         ret  = rte_telemetry_set_socket_nonblock(telemetry->server_fd);
233         if (ret < 0) {
234                 TELEMETRY_LOG_ERR("Could not set socket to NONBLOCK");
235                 goto close_socket;
236         }
237
238         addr.sun_family = AF_UNIX;
239         rte_telemetry_get_runtime_dir(socket_path, sizeof(socket_path));
240         strlcpy(addr.sun_path, socket_path, sizeof(addr.sun_path));
241         unlink(socket_path);
242
243         if (bind(telemetry->server_fd, (struct sockaddr *)&addr,
244                 sizeof(addr)) < 0) {
245                 TELEMETRY_LOG_ERR("Socket binding error");
246                 goto close_socket;
247         }
248
249         return 0;
250
251 close_socket:
252         if (close(telemetry->server_fd) < 0) {
253                 TELEMETRY_LOG_ERR("Close TELEMETRY socket failed");
254                 return -EPERM;
255         }
256
257         return -1;
258 }
259
260 int32_t __rte_experimental
261 rte_telemetry_init()
262 {
263         int ret;
264         pthread_attr_t attr;
265         const char *telemetry_ctrl_thread = "telemetry";
266
267         if (static_telemetry) {
268                 TELEMETRY_LOG_WARN("TELEMETRY structure already initialised");
269                 return -EALREADY;
270         }
271
272         static_telemetry = calloc(1, sizeof(struct telemetry_impl));
273         if (static_telemetry == NULL) {
274                 TELEMETRY_LOG_ERR("Memory could not be allocated");
275                 return -ENOMEM;
276         }
277
278         static_telemetry->socket_id = rte_socket_id();
279         rte_metrics_init(static_telemetry->socket_id);
280
281         ret = pthread_attr_init(&attr);
282         if (ret != 0) {
283                 TELEMETRY_LOG_ERR("Pthread attribute init failed");
284                 return -EPERM;
285         }
286
287         ret = rte_telemetry_create_socket(static_telemetry);
288         if (ret < 0) {
289                 ret = rte_telemetry_cleanup();
290                 if (ret < 0)
291                         TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
292                 return -EPERM;
293         }
294
295         ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
296                 telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
297                 (void *)static_telemetry);
298         static_telemetry->thread_status = 1;
299
300         if (ret < 0) {
301                 ret = rte_telemetry_cleanup();
302                 if (ret < 0)
303                         TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
304                 return -EPERM;
305         }
306
307         return 0;
308 }
309
310 int32_t __rte_experimental
311 rte_telemetry_cleanup(void)
312 {
313         int ret;
314         struct telemetry_impl *telemetry = static_telemetry;
315
316         ret = close(telemetry->server_fd);
317         if (ret < 0) {
318                 TELEMETRY_LOG_ERR("Close TELEMETRY socket failed");
319                 free(telemetry);
320                 return -EPERM;
321         }
322
323         telemetry->thread_status = 0;
324         pthread_join(telemetry->thread_id, NULL);
325         free(telemetry);
326         static_telemetry = NULL;
327
328         return 0;
329 }
330
331 int telemetry_log_level;
332
333 static struct rte_option option = {
334         .opt_str = "--telemetry",
335         .cb = &rte_telemetry_init,
336         .enabled = 0
337 };
338
339 RTE_INIT(rte_telemetry_register)
340 {
341         telemetry_log_level = rte_log_register("lib.telemetry");
342         if (telemetry_log_level >= 0)
343                 rte_log_set_level(telemetry_log_level, RTE_LOG_ERR);
344
345         rte_option_register(&option);
346 }