net/failsafe: use SPDX tags in 6WIND copyrighted files
[dpdk.git] / drivers / net / failsafe / failsafe_args.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2017 6WIND S.A.
3  * Copyright 2017 Mellanox.
4  */
5
6 #include <fcntl.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <errno.h>
12
13 #include <rte_debug.h>
14 #include <rte_devargs.h>
15 #include <rte_malloc.h>
16 #include <rte_kvargs.h>
17
18 #include "failsafe_private.h"
19
20 /* Callback used when a new device is found in devargs */
21 typedef int (parse_cb)(struct rte_eth_dev *dev, const char *params,
22                 uint8_t head);
23
24 uint64_t hotplug_poll = FAILSAFE_HOTPLUG_DEFAULT_TIMEOUT_MS;
25 int mac_from_arg = 0;
26
27 const char *pmd_failsafe_init_parameters[] = {
28         PMD_FAILSAFE_HOTPLUG_POLL_KVARG,
29         PMD_FAILSAFE_MAC_KVARG,
30         NULL,
31 };
32
33 /*
34  * input: text.
35  * output: 0: if text[0] != '(',
36  *         0: if there are no corresponding ')'
37  *         n: distance to corresponding ')' otherwise
38  */
39 static size_t
40 closing_paren(const char *text)
41 {
42         int nb_open = 0;
43         size_t i = 0;
44
45         while (text[i] != '\0') {
46                 if (text[i] == '(')
47                         nb_open++;
48                 if (text[i] == ')')
49                         nb_open--;
50                 if (nb_open == 0)
51                         return i;
52                 i++;
53         }
54         return 0;
55 }
56
57 static int
58 fs_parse_device(struct sub_device *sdev, char *args)
59 {
60         struct rte_devargs *d;
61         int ret;
62
63         d = &sdev->devargs;
64         DEBUG("%s", args);
65         ret = rte_eal_devargs_parse(args, d);
66         if (ret) {
67                 DEBUG("devargs parsing failed with code %d", ret);
68                 return ret;
69         }
70         sdev->bus = d->bus;
71         sdev->state = DEV_PARSED;
72         return 0;
73 }
74
75 static void
76 fs_sanitize_cmdline(char *args)
77 {
78         char *nl;
79
80         nl = strrchr(args, '\n');
81         if (nl)
82                 nl[0] = '\0';
83 }
84
85 static int
86 fs_execute_cmd(struct sub_device *sdev, char *cmdline)
87 {
88         FILE *fp;
89         /* store possible newline as well */
90         char output[DEVARGS_MAXLEN + 1];
91         size_t len;
92         int ret;
93
94         RTE_ASSERT(cmdline != NULL || sdev->cmdline != NULL);
95         if (sdev->cmdline == NULL) {
96                 size_t i;
97
98                 len = strlen(cmdline) + 1;
99                 sdev->cmdline = calloc(1, len);
100                 if (sdev->cmdline == NULL) {
101                         ERROR("Command line allocation failed");
102                         return -ENOMEM;
103                 }
104                 snprintf(sdev->cmdline, len, "%s", cmdline);
105                 /* Replace all commas in the command line by spaces */
106                 for (i = 0; i < len; i++)
107                         if (sdev->cmdline[i] == ',')
108                                 sdev->cmdline[i] = ' ';
109         }
110         DEBUG("'%s'", sdev->cmdline);
111         fp = popen(sdev->cmdline, "r");
112         if (fp == NULL) {
113                 ret = -errno;
114                 ERROR("popen: %s", strerror(errno));
115                 return ret;
116         }
117         /* We only read one line */
118         if (fgets(output, sizeof(output) - 1, fp) == NULL) {
119                 DEBUG("Could not read command output");
120                 ret = -ENODEV;
121                 goto ret_pclose;
122         }
123         fs_sanitize_cmdline(output);
124         if (output[0] == '\0') {
125                 ret = -ENODEV;
126                 goto ret_pclose;
127         }
128         ret = fs_parse_device(sdev, output);
129         if (ret)
130                 ERROR("Parsing device '%s' failed", output);
131 ret_pclose:
132         if (pclose(fp) == -1)
133                 ERROR("pclose: %s", strerror(errno));
134         return ret;
135 }
136
137 static int
138 fs_read_fd(struct sub_device *sdev, char *fd_str)
139 {
140         FILE *fp = NULL;
141         int fd = -1;
142         /* store possible newline as well */
143         char output[DEVARGS_MAXLEN + 1];
144         int err = -ENODEV;
145         int oflags;
146         int lcount;
147
148         RTE_ASSERT(fd_str != NULL || sdev->fd_str != NULL);
149         if (sdev->fd_str == NULL) {
150                 sdev->fd_str = strdup(fd_str);
151                 if (sdev->fd_str == NULL) {
152                         ERROR("Command line allocation failed");
153                         return -ENOMEM;
154                 }
155         }
156         errno = 0;
157         fd = strtol(fd_str, &fd_str, 0);
158         if (errno || *fd_str || fd < 0) {
159                 ERROR("Parsing FD number failed");
160                 goto error;
161         }
162         /* Fiddle with copy of file descriptor */
163         fd = dup(fd);
164         if (fd == -1)
165                 goto error;
166         oflags = fcntl(fd, F_GETFL);
167         if (oflags == -1)
168                 goto error;
169         if (fcntl(fd, F_SETFL, oflags | O_NONBLOCK) == -1)
170                 goto error;
171         fp = fdopen(fd, "r");
172         if (fp == NULL)
173                 goto error;
174         fd = -1;
175         /* Only take the last line into account */
176         lcount = 0;
177         while (fgets(output, sizeof(output), fp))
178                 ++lcount;
179         if (lcount == 0)
180                 goto error;
181         else if (ferror(fp) && errno != EAGAIN)
182                 goto error;
183         /* Line must end with a newline character */
184         fs_sanitize_cmdline(output);
185         if (output[0] == '\0')
186                 goto error;
187         err = fs_parse_device(sdev, output);
188         if (err)
189                 ERROR("Parsing device '%s' failed", output);
190 error:
191         if (fp)
192                 fclose(fp);
193         if (fd != -1)
194                 close(fd);
195         return err;
196 }
197
198 static int
199 fs_parse_device_param(struct rte_eth_dev *dev, const char *param,
200                 uint8_t head)
201 {
202         struct fs_priv *priv;
203         struct sub_device *sdev;
204         char *args = NULL;
205         size_t a, b;
206         int ret;
207
208         priv = PRIV(dev);
209         a = 0;
210         b = 0;
211         ret = 0;
212         while  (param[b] != '(' &&
213                 param[b] != '\0')
214                 b++;
215         a = b;
216         b += closing_paren(&param[b]);
217         if (a == b) {
218                 ERROR("Dangling parenthesis");
219                 return -EINVAL;
220         }
221         a += 1;
222         args = strndup(&param[a], b - a);
223         if (args == NULL) {
224                 ERROR("Not enough memory for parameter parsing");
225                 return -ENOMEM;
226         }
227         sdev = &priv->subs[head];
228         if (strncmp(param, "dev", 3) == 0) {
229                 ret = fs_parse_device(sdev, args);
230                 if (ret)
231                         goto free_args;
232         } else if (strncmp(param, "exec", 4) == 0) {
233                 ret = fs_execute_cmd(sdev, args);
234                 if (ret == -ENODEV) {
235                         DEBUG("Reading device info from command line failed");
236                         ret = 0;
237                 }
238                 if (ret)
239                         goto free_args;
240         } else if (strncmp(param, "fd(", 3) == 0) {
241                 ret = fs_read_fd(sdev, args);
242                 if (ret == -ENODEV) {
243                         DEBUG("Reading device info from FD failed");
244                         ret = 0;
245                 }
246                 if (ret)
247                         goto free_args;
248         } else {
249                 ERROR("Unrecognized device type: %.*s", (int)b, param);
250                 return -EINVAL;
251         }
252 free_args:
253         free(args);
254         return ret;
255 }
256
257 static int
258 fs_parse_sub_devices(parse_cb *cb,
259                 struct rte_eth_dev *dev, const char *params)
260 {
261         size_t a, b;
262         uint8_t head;
263         int ret;
264
265         a = 0;
266         head = 0;
267         ret = 0;
268         while (params[a] != '\0') {
269                 b = a;
270                 while (params[b] != '(' &&
271                        params[b] != ',' &&
272                        params[b] != '\0')
273                         b++;
274                 if (b == a) {
275                         ERROR("Invalid parameter");
276                         return -EINVAL;
277                 }
278                 if (params[b] == ',') {
279                         a = b + 1;
280                         continue;
281                 }
282                 if (params[b] == '(') {
283                         size_t start = b;
284
285                         b += closing_paren(&params[b]);
286                         if (b == start) {
287                                 ERROR("Dangling parenthesis");
288                                 return -EINVAL;
289                         }
290                         ret = (*cb)(dev, &params[a], head);
291                         if (ret)
292                                 return ret;
293                         head += 1;
294                         b += 1;
295                         if (params[b] == '\0')
296                                 return 0;
297                 }
298                 a = b + 1;
299         }
300         return 0;
301 }
302
303 static int
304 fs_remove_sub_devices_definition(char params[DEVARGS_MAXLEN])
305 {
306         char buffer[DEVARGS_MAXLEN] = {0};
307         size_t a, b;
308         int i;
309
310         a = 0;
311         i = 0;
312         while (params[a] != '\0') {
313                 b = a;
314                 while (params[b] != '(' &&
315                        params[b] != ',' &&
316                        params[b] != '\0')
317                         b++;
318                 if (b == a) {
319                         ERROR("Invalid parameter");
320                         return -EINVAL;
321                 }
322                 if (params[b] == ',' || params[b] == '\0') {
323                         size_t len = b - a;
324
325                         if (i > 0)
326                                 len += 1;
327                         snprintf(&buffer[i], len + 1, "%s%s",
328                                         i ? "," : "", &params[a]);
329                         i += len;
330                 } else if (params[b] == '(') {
331                         size_t start = b;
332
333                         b += closing_paren(&params[b]);
334                         if (b == start)
335                                 return -EINVAL;
336                         b += 1;
337                         if (params[b] == '\0')
338                                 goto out;
339                 }
340                 a = b + 1;
341         }
342 out:
343         snprintf(params, DEVARGS_MAXLEN, "%s", buffer);
344         return 0;
345 }
346
347 static int
348 fs_get_u64_arg(const char *key __rte_unused,
349                 const char *value, void *out)
350 {
351         uint64_t *u64 = out;
352         char *endptr = NULL;
353
354         if ((value == NULL) || (out == NULL))
355                 return -EINVAL;
356         errno = 0;
357         *u64 = strtoull(value, &endptr, 0);
358         if (errno != 0)
359                 return -errno;
360         if (endptr == value)
361                 return -1;
362         return 0;
363 }
364
365 static int
366 fs_get_mac_addr_arg(const char *key __rte_unused,
367                 const char *value, void *out)
368 {
369         struct ether_addr *ea = out;
370         int ret;
371
372         if ((value == NULL) || (out == NULL))
373                 return -EINVAL;
374         ret = sscanf(value, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
375                 &ea->addr_bytes[0], &ea->addr_bytes[1],
376                 &ea->addr_bytes[2], &ea->addr_bytes[3],
377                 &ea->addr_bytes[4], &ea->addr_bytes[5]);
378         return ret != ETHER_ADDR_LEN;
379 }
380
381 int
382 failsafe_args_parse(struct rte_eth_dev *dev, const char *params)
383 {
384         struct fs_priv *priv;
385         char mut_params[DEVARGS_MAXLEN] = "";
386         struct rte_kvargs *kvlist = NULL;
387         unsigned int arg_count;
388         size_t n;
389         int ret;
390
391         priv = PRIV(dev);
392         ret = 0;
393         priv->subs_tx = FAILSAFE_MAX_ETHPORTS;
394         /* default parameters */
395         n = snprintf(mut_params, sizeof(mut_params), "%s", params);
396         if (n >= sizeof(mut_params)) {
397                 ERROR("Parameter string too long (>=%zu)",
398                                 sizeof(mut_params));
399                 return -ENOMEM;
400         }
401         ret = fs_parse_sub_devices(fs_parse_device_param,
402                                    dev, params);
403         if (ret < 0)
404                 return ret;
405         ret = fs_remove_sub_devices_definition(mut_params);
406         if (ret < 0)
407                 return ret;
408         if (strnlen(mut_params, sizeof(mut_params)) > 0) {
409                 kvlist = rte_kvargs_parse(mut_params,
410                                 pmd_failsafe_init_parameters);
411                 if (kvlist == NULL) {
412                         ERROR("Error parsing parameters, usage:\n"
413                                 PMD_FAILSAFE_PARAM_STRING);
414                         return -1;
415                 }
416                 /* PLUG_IN event poll timer */
417                 arg_count = rte_kvargs_count(kvlist,
418                                 PMD_FAILSAFE_HOTPLUG_POLL_KVARG);
419                 if (arg_count == 1) {
420                         ret = rte_kvargs_process(kvlist,
421                                         PMD_FAILSAFE_HOTPLUG_POLL_KVARG,
422                                         &fs_get_u64_arg, &hotplug_poll);
423                         if (ret < 0)
424                                 goto free_kvlist;
425                 }
426                 /* MAC addr */
427                 arg_count = rte_kvargs_count(kvlist,
428                                 PMD_FAILSAFE_MAC_KVARG);
429                 if (arg_count > 0) {
430                         ret = rte_kvargs_process(kvlist,
431                                         PMD_FAILSAFE_MAC_KVARG,
432                                         &fs_get_mac_addr_arg,
433                                         &dev->data->mac_addrs[0]);
434                         if (ret < 0)
435                                 goto free_kvlist;
436
437                         mac_from_arg = 1;
438                 }
439         }
440         PRIV(dev)->state = DEV_PARSED;
441 free_kvlist:
442         rte_kvargs_free(kvlist);
443         return ret;
444 }
445
446 void
447 failsafe_args_free(struct rte_eth_dev *dev)
448 {
449         struct sub_device *sdev;
450         uint8_t i;
451
452         FOREACH_SUBDEV(sdev, i, dev) {
453                 free(sdev->cmdline);
454                 sdev->cmdline = NULL;
455                 free(sdev->fd_str);
456                 sdev->fd_str = NULL;
457                 free(sdev->devargs.args);
458                 sdev->devargs.args = NULL;
459         }
460 }
461
462 static int
463 fs_count_device(struct rte_eth_dev *dev, const char *param,
464                 uint8_t head __rte_unused)
465 {
466         size_t b = 0;
467
468         while  (param[b] != '(' &&
469                 param[b] != '\0')
470                 b++;
471         if (strncmp(param, "dev", b) != 0 &&
472             strncmp(param, "exec", b) != 0 &&
473             strncmp(param, "fd(", b) != 0) {
474                 ERROR("Unrecognized device type: %.*s", (int)b, param);
475                 return -EINVAL;
476         }
477         PRIV(dev)->subs_tail += 1;
478         return 0;
479 }
480
481 int
482 failsafe_args_count_subdevice(struct rte_eth_dev *dev,
483                         const char *params)
484 {
485         return fs_parse_sub_devices(fs_count_device,
486                                     dev, params);
487 }
488
489 static int
490 fs_parse_sub_device(struct sub_device *sdev)
491 {
492         struct rte_devargs *da;
493         char devstr[DEVARGS_MAXLEN] = "";
494
495         da = &sdev->devargs;
496         snprintf(devstr, sizeof(devstr), "%s,%s", da->name, da->args);
497         return fs_parse_device(sdev, devstr);
498 }
499
500 int
501 failsafe_args_parse_subs(struct rte_eth_dev *dev)
502 {
503         struct sub_device *sdev;
504         uint8_t i;
505         int ret = 0;
506
507         FOREACH_SUBDEV(sdev, i, dev) {
508                 if (sdev->state >= DEV_PARSED)
509                         continue;
510                 if (sdev->cmdline)
511                         ret = fs_execute_cmd(sdev, sdev->cmdline);
512                 else if (sdev->fd_str)
513                         ret = fs_read_fd(sdev, sdev->fd_str);
514                 else
515                         ret = fs_parse_sub_device(sdev);
516                 if (ret == 0)
517                         sdev->state = DEV_PARSED;
518         }
519         return 0;
520 }