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