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