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