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