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