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