1*d30ea906Sjfb8856606 /* SPDX-License-Identifier: BSD-3-Clause
22bfe3f2eSlogwang  * Copyright 2017 6WIND S.A.
3*d30ea906Sjfb8856606  * Copyright 2017 Mellanox Technologies, Ltd
42bfe3f2eSlogwang  */
52bfe3f2eSlogwang 
6*d30ea906Sjfb8856606 #include <fcntl.h>
7*d30ea906Sjfb8856606 #include <stdio.h>
8*d30ea906Sjfb8856606 #include <stdlib.h>
92bfe3f2eSlogwang #include <string.h>
10*d30ea906Sjfb8856606 #include <unistd.h>
112bfe3f2eSlogwang #include <errno.h>
122bfe3f2eSlogwang 
132bfe3f2eSlogwang #include <rte_debug.h>
142bfe3f2eSlogwang #include <rte_devargs.h>
152bfe3f2eSlogwang #include <rte_malloc.h>
162bfe3f2eSlogwang #include <rte_kvargs.h>
17*d30ea906Sjfb8856606 #include <rte_string_fns.h>
182bfe3f2eSlogwang 
192bfe3f2eSlogwang #include "failsafe_private.h"
202bfe3f2eSlogwang 
212bfe3f2eSlogwang /* Callback used when a new device is found in devargs */
222bfe3f2eSlogwang typedef int (parse_cb)(struct rte_eth_dev *dev, const char *params,
232bfe3f2eSlogwang 		uint8_t head);
242bfe3f2eSlogwang 
25*d30ea906Sjfb8856606 uint64_t failsafe_hotplug_poll = FAILSAFE_HOTPLUG_DEFAULT_TIMEOUT_MS;
26*d30ea906Sjfb8856606 int failsafe_mac_from_arg;
272bfe3f2eSlogwang 
28*d30ea906Sjfb8856606 static const char * const pmd_failsafe_init_parameters[] = {
292bfe3f2eSlogwang 	PMD_FAILSAFE_HOTPLUG_POLL_KVARG,
302bfe3f2eSlogwang 	PMD_FAILSAFE_MAC_KVARG,
312bfe3f2eSlogwang 	NULL,
322bfe3f2eSlogwang };
332bfe3f2eSlogwang 
342bfe3f2eSlogwang /*
352bfe3f2eSlogwang  * input: text.
362bfe3f2eSlogwang  * output: 0: if text[0] != '(',
372bfe3f2eSlogwang  *         0: if there are no corresponding ')'
382bfe3f2eSlogwang  *         n: distance to corresponding ')' otherwise
392bfe3f2eSlogwang  */
402bfe3f2eSlogwang static size_t
412bfe3f2eSlogwang closing_paren(const char *text)
422bfe3f2eSlogwang {
432bfe3f2eSlogwang 	int nb_open = 0;
442bfe3f2eSlogwang 	size_t i = 0;
452bfe3f2eSlogwang 
462bfe3f2eSlogwang 	while (text[i] != '\0') {
472bfe3f2eSlogwang 		if (text[i] == '(')
482bfe3f2eSlogwang 			nb_open++;
492bfe3f2eSlogwang 		if (text[i] == ')')
502bfe3f2eSlogwang 			nb_open--;
512bfe3f2eSlogwang 		if (nb_open == 0)
522bfe3f2eSlogwang 			return i;
532bfe3f2eSlogwang 		i++;
542bfe3f2eSlogwang 	}
552bfe3f2eSlogwang 	return 0;
562bfe3f2eSlogwang }
572bfe3f2eSlogwang 
582bfe3f2eSlogwang static int
592bfe3f2eSlogwang fs_parse_device(struct sub_device *sdev, char *args)
602bfe3f2eSlogwang {
612bfe3f2eSlogwang 	struct rte_devargs *d;
622bfe3f2eSlogwang 	int ret;
632bfe3f2eSlogwang 
642bfe3f2eSlogwang 	d = &sdev->devargs;
652bfe3f2eSlogwang 	DEBUG("%s", args);
66*d30ea906Sjfb8856606 	ret = rte_devargs_parse(d, args);
672bfe3f2eSlogwang 	if (ret) {
682bfe3f2eSlogwang 		DEBUG("devargs parsing failed with code %d", ret);
692bfe3f2eSlogwang 		return ret;
702bfe3f2eSlogwang 	}
712bfe3f2eSlogwang 	sdev->bus = d->bus;
722bfe3f2eSlogwang 	sdev->state = DEV_PARSED;
732bfe3f2eSlogwang 	return 0;
742bfe3f2eSlogwang }
752bfe3f2eSlogwang 
762bfe3f2eSlogwang static void
772bfe3f2eSlogwang fs_sanitize_cmdline(char *args)
782bfe3f2eSlogwang {
792bfe3f2eSlogwang 	char *nl;
802bfe3f2eSlogwang 
812bfe3f2eSlogwang 	nl = strrchr(args, '\n');
822bfe3f2eSlogwang 	if (nl)
832bfe3f2eSlogwang 		nl[0] = '\0';
842bfe3f2eSlogwang }
852bfe3f2eSlogwang 
862bfe3f2eSlogwang static int
872bfe3f2eSlogwang fs_execute_cmd(struct sub_device *sdev, char *cmdline)
882bfe3f2eSlogwang {
892bfe3f2eSlogwang 	FILE *fp;
902bfe3f2eSlogwang 	/* store possible newline as well */
912bfe3f2eSlogwang 	char output[DEVARGS_MAXLEN + 1];
922bfe3f2eSlogwang 	size_t len;
932bfe3f2eSlogwang 	int ret;
942bfe3f2eSlogwang 
952bfe3f2eSlogwang 	RTE_ASSERT(cmdline != NULL || sdev->cmdline != NULL);
962bfe3f2eSlogwang 	if (sdev->cmdline == NULL) {
972bfe3f2eSlogwang 		size_t i;
982bfe3f2eSlogwang 
992bfe3f2eSlogwang 		len = strlen(cmdline) + 1;
1002bfe3f2eSlogwang 		sdev->cmdline = calloc(1, len);
1012bfe3f2eSlogwang 		if (sdev->cmdline == NULL) {
1022bfe3f2eSlogwang 			ERROR("Command line allocation failed");
1032bfe3f2eSlogwang 			return -ENOMEM;
1042bfe3f2eSlogwang 		}
1052bfe3f2eSlogwang 		snprintf(sdev->cmdline, len, "%s", cmdline);
1062bfe3f2eSlogwang 		/* Replace all commas in the command line by spaces */
1072bfe3f2eSlogwang 		for (i = 0; i < len; i++)
1082bfe3f2eSlogwang 			if (sdev->cmdline[i] == ',')
1092bfe3f2eSlogwang 				sdev->cmdline[i] = ' ';
1102bfe3f2eSlogwang 	}
1112bfe3f2eSlogwang 	DEBUG("'%s'", sdev->cmdline);
1122bfe3f2eSlogwang 	fp = popen(sdev->cmdline, "r");
1132bfe3f2eSlogwang 	if (fp == NULL) {
1142bfe3f2eSlogwang 		ret = -errno;
1152bfe3f2eSlogwang 		ERROR("popen: %s", strerror(errno));
1162bfe3f2eSlogwang 		return ret;
1172bfe3f2eSlogwang 	}
1182bfe3f2eSlogwang 	/* We only read one line */
1192bfe3f2eSlogwang 	if (fgets(output, sizeof(output) - 1, fp) == NULL) {
1202bfe3f2eSlogwang 		DEBUG("Could not read command output");
1212bfe3f2eSlogwang 		ret = -ENODEV;
1222bfe3f2eSlogwang 		goto ret_pclose;
1232bfe3f2eSlogwang 	}
1242bfe3f2eSlogwang 	fs_sanitize_cmdline(output);
1252bfe3f2eSlogwang 	if (output[0] == '\0') {
1262bfe3f2eSlogwang 		ret = -ENODEV;
1272bfe3f2eSlogwang 		goto ret_pclose;
1282bfe3f2eSlogwang 	}
1292bfe3f2eSlogwang 	ret = fs_parse_device(sdev, output);
1302bfe3f2eSlogwang 	if (ret)
1312bfe3f2eSlogwang 		ERROR("Parsing device '%s' failed", output);
1322bfe3f2eSlogwang ret_pclose:
1332bfe3f2eSlogwang 	if (pclose(fp) == -1)
1342bfe3f2eSlogwang 		ERROR("pclose: %s", strerror(errno));
1352bfe3f2eSlogwang 	return ret;
1362bfe3f2eSlogwang }
1372bfe3f2eSlogwang 
1382bfe3f2eSlogwang static int
139*d30ea906Sjfb8856606 fs_read_fd(struct sub_device *sdev, char *fd_str)
140*d30ea906Sjfb8856606 {
141*d30ea906Sjfb8856606 	FILE *fp = NULL;
142*d30ea906Sjfb8856606 	int fd = -1;
143*d30ea906Sjfb8856606 	/* store possible newline as well */
144*d30ea906Sjfb8856606 	char output[DEVARGS_MAXLEN + 1];
145*d30ea906Sjfb8856606 	int err = -ENODEV;
146*d30ea906Sjfb8856606 	int oflags;
147*d30ea906Sjfb8856606 	int lcount;
148*d30ea906Sjfb8856606 
149*d30ea906Sjfb8856606 	RTE_ASSERT(fd_str != NULL || sdev->fd_str != NULL);
150*d30ea906Sjfb8856606 	if (sdev->fd_str == NULL) {
151*d30ea906Sjfb8856606 		sdev->fd_str = strdup(fd_str);
152*d30ea906Sjfb8856606 		if (sdev->fd_str == NULL) {
153*d30ea906Sjfb8856606 			ERROR("Command line allocation failed");
154*d30ea906Sjfb8856606 			return -ENOMEM;
155*d30ea906Sjfb8856606 		}
156*d30ea906Sjfb8856606 	}
157*d30ea906Sjfb8856606 	errno = 0;
158*d30ea906Sjfb8856606 	fd = strtol(fd_str, &fd_str, 0);
159*d30ea906Sjfb8856606 	if (errno || *fd_str || fd < 0) {
160*d30ea906Sjfb8856606 		ERROR("Parsing FD number failed");
161*d30ea906Sjfb8856606 		goto error;
162*d30ea906Sjfb8856606 	}
163*d30ea906Sjfb8856606 	/* Fiddle with copy of file descriptor */
164*d30ea906Sjfb8856606 	fd = dup(fd);
165*d30ea906Sjfb8856606 	if (fd == -1)
166*d30ea906Sjfb8856606 		goto error;
167*d30ea906Sjfb8856606 	oflags = fcntl(fd, F_GETFL);
168*d30ea906Sjfb8856606 	if (oflags == -1)
169*d30ea906Sjfb8856606 		goto error;
170*d30ea906Sjfb8856606 	if (fcntl(fd, F_SETFL, oflags | O_NONBLOCK) == -1)
171*d30ea906Sjfb8856606 		goto error;
172*d30ea906Sjfb8856606 	fp = fdopen(fd, "r");
173*d30ea906Sjfb8856606 	if (fp == NULL)
174*d30ea906Sjfb8856606 		goto error;
175*d30ea906Sjfb8856606 	fd = -1;
176*d30ea906Sjfb8856606 	/* Only take the last line into account */
177*d30ea906Sjfb8856606 	lcount = 0;
178*d30ea906Sjfb8856606 	while (fgets(output, sizeof(output), fp))
179*d30ea906Sjfb8856606 		++lcount;
180*d30ea906Sjfb8856606 	if (lcount == 0)
181*d30ea906Sjfb8856606 		goto error;
182*d30ea906Sjfb8856606 	else if (ferror(fp) && errno != EAGAIN)
183*d30ea906Sjfb8856606 		goto error;
184*d30ea906Sjfb8856606 	/* Line must end with a newline character */
185*d30ea906Sjfb8856606 	fs_sanitize_cmdline(output);
186*d30ea906Sjfb8856606 	if (output[0] == '\0')
187*d30ea906Sjfb8856606 		goto error;
188*d30ea906Sjfb8856606 	err = fs_parse_device(sdev, output);
189*d30ea906Sjfb8856606 	if (err)
190*d30ea906Sjfb8856606 		ERROR("Parsing device '%s' failed", output);
191*d30ea906Sjfb8856606 error:
192*d30ea906Sjfb8856606 	if (fp)
193*d30ea906Sjfb8856606 		fclose(fp);
194*d30ea906Sjfb8856606 	if (fd != -1)
195*d30ea906Sjfb8856606 		close(fd);
196*d30ea906Sjfb8856606 	return err;
197*d30ea906Sjfb8856606 }
198*d30ea906Sjfb8856606 
199*d30ea906Sjfb8856606 static int
2002bfe3f2eSlogwang fs_parse_device_param(struct rte_eth_dev *dev, const char *param,
2012bfe3f2eSlogwang 		uint8_t head)
2022bfe3f2eSlogwang {
2032bfe3f2eSlogwang 	struct fs_priv *priv;
2042bfe3f2eSlogwang 	struct sub_device *sdev;
2052bfe3f2eSlogwang 	char *args = NULL;
2062bfe3f2eSlogwang 	size_t a, b;
2072bfe3f2eSlogwang 	int ret;
2082bfe3f2eSlogwang 
2092bfe3f2eSlogwang 	priv = PRIV(dev);
2102bfe3f2eSlogwang 	a = 0;
2112bfe3f2eSlogwang 	b = 0;
2122bfe3f2eSlogwang 	ret = 0;
2132bfe3f2eSlogwang 	while  (param[b] != '(' &&
2142bfe3f2eSlogwang 		param[b] != '\0')
2152bfe3f2eSlogwang 		b++;
2162bfe3f2eSlogwang 	a = b;
2172bfe3f2eSlogwang 	b += closing_paren(&param[b]);
2182bfe3f2eSlogwang 	if (a == b) {
2192bfe3f2eSlogwang 		ERROR("Dangling parenthesis");
2202bfe3f2eSlogwang 		return -EINVAL;
2212bfe3f2eSlogwang 	}
2222bfe3f2eSlogwang 	a += 1;
2232bfe3f2eSlogwang 	args = strndup(&param[a], b - a);
2242bfe3f2eSlogwang 	if (args == NULL) {
2252bfe3f2eSlogwang 		ERROR("Not enough memory for parameter parsing");
2262bfe3f2eSlogwang 		return -ENOMEM;
2272bfe3f2eSlogwang 	}
2282bfe3f2eSlogwang 	sdev = &priv->subs[head];
2292bfe3f2eSlogwang 	if (strncmp(param, "dev", 3) == 0) {
2302bfe3f2eSlogwang 		ret = fs_parse_device(sdev, args);
2312bfe3f2eSlogwang 		if (ret)
2322bfe3f2eSlogwang 			goto free_args;
2332bfe3f2eSlogwang 	} else if (strncmp(param, "exec", 4) == 0) {
2342bfe3f2eSlogwang 		ret = fs_execute_cmd(sdev, args);
2352bfe3f2eSlogwang 		if (ret == -ENODEV) {
2362bfe3f2eSlogwang 			DEBUG("Reading device info from command line failed");
2372bfe3f2eSlogwang 			ret = 0;
2382bfe3f2eSlogwang 		}
2392bfe3f2eSlogwang 		if (ret)
2402bfe3f2eSlogwang 			goto free_args;
241*d30ea906Sjfb8856606 	} else if (strncmp(param, "fd(", 3) == 0) {
242*d30ea906Sjfb8856606 		ret = fs_read_fd(sdev, args);
243*d30ea906Sjfb8856606 		if (ret == -ENODEV) {
244*d30ea906Sjfb8856606 			DEBUG("Reading device info from FD failed");
245*d30ea906Sjfb8856606 			ret = 0;
246*d30ea906Sjfb8856606 		}
247*d30ea906Sjfb8856606 		if (ret)
248*d30ea906Sjfb8856606 			goto free_args;
2492bfe3f2eSlogwang 	} else {
2502bfe3f2eSlogwang 		ERROR("Unrecognized device type: %.*s", (int)b, param);
2512bfe3f2eSlogwang 		return -EINVAL;
2522bfe3f2eSlogwang 	}
2532bfe3f2eSlogwang free_args:
2542bfe3f2eSlogwang 	free(args);
2552bfe3f2eSlogwang 	return ret;
2562bfe3f2eSlogwang }
2572bfe3f2eSlogwang 
2582bfe3f2eSlogwang static int
2592bfe3f2eSlogwang fs_parse_sub_devices(parse_cb *cb,
2602bfe3f2eSlogwang 		struct rte_eth_dev *dev, const char *params)
2612bfe3f2eSlogwang {
2622bfe3f2eSlogwang 	size_t a, b;
2632bfe3f2eSlogwang 	uint8_t head;
2642bfe3f2eSlogwang 	int ret;
2652bfe3f2eSlogwang 
2662bfe3f2eSlogwang 	a = 0;
2672bfe3f2eSlogwang 	head = 0;
2682bfe3f2eSlogwang 	ret = 0;
2692bfe3f2eSlogwang 	while (params[a] != '\0') {
2702bfe3f2eSlogwang 		b = a;
2712bfe3f2eSlogwang 		while (params[b] != '(' &&
2722bfe3f2eSlogwang 		       params[b] != ',' &&
2732bfe3f2eSlogwang 		       params[b] != '\0')
2742bfe3f2eSlogwang 			b++;
2752bfe3f2eSlogwang 		if (b == a) {
2762bfe3f2eSlogwang 			ERROR("Invalid parameter");
2772bfe3f2eSlogwang 			return -EINVAL;
2782bfe3f2eSlogwang 		}
2792bfe3f2eSlogwang 		if (params[b] == ',') {
2802bfe3f2eSlogwang 			a = b + 1;
2812bfe3f2eSlogwang 			continue;
2822bfe3f2eSlogwang 		}
2832bfe3f2eSlogwang 		if (params[b] == '(') {
2842bfe3f2eSlogwang 			size_t start = b;
2852bfe3f2eSlogwang 
2862bfe3f2eSlogwang 			b += closing_paren(&params[b]);
2872bfe3f2eSlogwang 			if (b == start) {
2882bfe3f2eSlogwang 				ERROR("Dangling parenthesis");
2892bfe3f2eSlogwang 				return -EINVAL;
2902bfe3f2eSlogwang 			}
2912bfe3f2eSlogwang 			ret = (*cb)(dev, &params[a], head);
2922bfe3f2eSlogwang 			if (ret)
2932bfe3f2eSlogwang 				return ret;
2942bfe3f2eSlogwang 			head += 1;
2952bfe3f2eSlogwang 			b += 1;
2962bfe3f2eSlogwang 			if (params[b] == '\0')
2972bfe3f2eSlogwang 				return 0;
2982bfe3f2eSlogwang 		}
2992bfe3f2eSlogwang 		a = b + 1;
3002bfe3f2eSlogwang 	}
3012bfe3f2eSlogwang 	return 0;
3022bfe3f2eSlogwang }
3032bfe3f2eSlogwang 
3042bfe3f2eSlogwang static int
3052bfe3f2eSlogwang fs_remove_sub_devices_definition(char params[DEVARGS_MAXLEN])
3062bfe3f2eSlogwang {
3072bfe3f2eSlogwang 	char buffer[DEVARGS_MAXLEN] = {0};
3082bfe3f2eSlogwang 	size_t a, b;
3092bfe3f2eSlogwang 	int i;
3102bfe3f2eSlogwang 
3112bfe3f2eSlogwang 	a = 0;
3122bfe3f2eSlogwang 	i = 0;
3132bfe3f2eSlogwang 	while (params[a] != '\0') {
3142bfe3f2eSlogwang 		b = a;
3152bfe3f2eSlogwang 		while (params[b] != '(' &&
3162bfe3f2eSlogwang 		       params[b] != ',' &&
3172bfe3f2eSlogwang 		       params[b] != '\0')
3182bfe3f2eSlogwang 			b++;
3192bfe3f2eSlogwang 		if (b == a) {
3202bfe3f2eSlogwang 			ERROR("Invalid parameter");
3212bfe3f2eSlogwang 			return -EINVAL;
3222bfe3f2eSlogwang 		}
3232bfe3f2eSlogwang 		if (params[b] == ',' || params[b] == '\0') {
3242bfe3f2eSlogwang 			size_t len = b - a;
3252bfe3f2eSlogwang 
3262bfe3f2eSlogwang 			if (i > 0)
3272bfe3f2eSlogwang 				len += 1;
3282bfe3f2eSlogwang 			snprintf(&buffer[i], len + 1, "%s%s",
3292bfe3f2eSlogwang 					i ? "," : "", &params[a]);
3302bfe3f2eSlogwang 			i += len;
3312bfe3f2eSlogwang 		} else if (params[b] == '(') {
3322bfe3f2eSlogwang 			size_t start = b;
3332bfe3f2eSlogwang 
3342bfe3f2eSlogwang 			b += closing_paren(&params[b]);
3352bfe3f2eSlogwang 			if (b == start)
3362bfe3f2eSlogwang 				return -EINVAL;
3372bfe3f2eSlogwang 			b += 1;
3382bfe3f2eSlogwang 			if (params[b] == '\0')
3392bfe3f2eSlogwang 				goto out;
3402bfe3f2eSlogwang 		}
3412bfe3f2eSlogwang 		a = b + 1;
3422bfe3f2eSlogwang 	}
3432bfe3f2eSlogwang out:
344*d30ea906Sjfb8856606 	strlcpy(params, buffer, DEVARGS_MAXLEN);
3452bfe3f2eSlogwang 	return 0;
3462bfe3f2eSlogwang }
3472bfe3f2eSlogwang 
3482bfe3f2eSlogwang static int
3492bfe3f2eSlogwang fs_get_u64_arg(const char *key __rte_unused,
3502bfe3f2eSlogwang 		const char *value, void *out)
3512bfe3f2eSlogwang {
3522bfe3f2eSlogwang 	uint64_t *u64 = out;
3532bfe3f2eSlogwang 	char *endptr = NULL;
3542bfe3f2eSlogwang 
3552bfe3f2eSlogwang 	if ((value == NULL) || (out == NULL))
3562bfe3f2eSlogwang 		return -EINVAL;
3572bfe3f2eSlogwang 	errno = 0;
3582bfe3f2eSlogwang 	*u64 = strtoull(value, &endptr, 0);
3592bfe3f2eSlogwang 	if (errno != 0)
3602bfe3f2eSlogwang 		return -errno;
3612bfe3f2eSlogwang 	if (endptr == value)
3622bfe3f2eSlogwang 		return -1;
3632bfe3f2eSlogwang 	return 0;
3642bfe3f2eSlogwang }
3652bfe3f2eSlogwang 
3662bfe3f2eSlogwang static int
3672bfe3f2eSlogwang fs_get_mac_addr_arg(const char *key __rte_unused,
3682bfe3f2eSlogwang 		const char *value, void *out)
3692bfe3f2eSlogwang {
3702bfe3f2eSlogwang 	struct ether_addr *ea = out;
3712bfe3f2eSlogwang 	int ret;
3722bfe3f2eSlogwang 
3732bfe3f2eSlogwang 	if ((value == NULL) || (out == NULL))
3742bfe3f2eSlogwang 		return -EINVAL;
3752bfe3f2eSlogwang 	ret = sscanf(value, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
3762bfe3f2eSlogwang 		&ea->addr_bytes[0], &ea->addr_bytes[1],
3772bfe3f2eSlogwang 		&ea->addr_bytes[2], &ea->addr_bytes[3],
3782bfe3f2eSlogwang 		&ea->addr_bytes[4], &ea->addr_bytes[5]);
3792bfe3f2eSlogwang 	return ret != ETHER_ADDR_LEN;
3802bfe3f2eSlogwang }
3812bfe3f2eSlogwang 
3822bfe3f2eSlogwang int
3832bfe3f2eSlogwang failsafe_args_parse(struct rte_eth_dev *dev, const char *params)
3842bfe3f2eSlogwang {
3852bfe3f2eSlogwang 	struct fs_priv *priv;
3862bfe3f2eSlogwang 	char mut_params[DEVARGS_MAXLEN] = "";
3872bfe3f2eSlogwang 	struct rte_kvargs *kvlist = NULL;
3882bfe3f2eSlogwang 	unsigned int arg_count;
3892bfe3f2eSlogwang 	size_t n;
3902bfe3f2eSlogwang 	int ret;
3912bfe3f2eSlogwang 
3922bfe3f2eSlogwang 	priv = PRIV(dev);
3932bfe3f2eSlogwang 	ret = 0;
3942bfe3f2eSlogwang 	priv->subs_tx = FAILSAFE_MAX_ETHPORTS;
3952bfe3f2eSlogwang 	/* default parameters */
396*d30ea906Sjfb8856606 	n = strlcpy(mut_params, params, sizeof(mut_params));
3972bfe3f2eSlogwang 	if (n >= sizeof(mut_params)) {
3982bfe3f2eSlogwang 		ERROR("Parameter string too long (>=%zu)",
3992bfe3f2eSlogwang 				sizeof(mut_params));
4002bfe3f2eSlogwang 		return -ENOMEM;
4012bfe3f2eSlogwang 	}
4022bfe3f2eSlogwang 	ret = fs_parse_sub_devices(fs_parse_device_param,
4032bfe3f2eSlogwang 				   dev, params);
4042bfe3f2eSlogwang 	if (ret < 0)
4052bfe3f2eSlogwang 		return ret;
4062bfe3f2eSlogwang 	ret = fs_remove_sub_devices_definition(mut_params);
4072bfe3f2eSlogwang 	if (ret < 0)
4082bfe3f2eSlogwang 		return ret;
4092bfe3f2eSlogwang 	if (strnlen(mut_params, sizeof(mut_params)) > 0) {
4102bfe3f2eSlogwang 		kvlist = rte_kvargs_parse(mut_params,
4112bfe3f2eSlogwang 				pmd_failsafe_init_parameters);
4122bfe3f2eSlogwang 		if (kvlist == NULL) {
4132bfe3f2eSlogwang 			ERROR("Error parsing parameters, usage:\n"
4142bfe3f2eSlogwang 				PMD_FAILSAFE_PARAM_STRING);
4152bfe3f2eSlogwang 			return -1;
4162bfe3f2eSlogwang 		}
4172bfe3f2eSlogwang 		/* PLUG_IN event poll timer */
4182bfe3f2eSlogwang 		arg_count = rte_kvargs_count(kvlist,
4192bfe3f2eSlogwang 				PMD_FAILSAFE_HOTPLUG_POLL_KVARG);
4202bfe3f2eSlogwang 		if (arg_count == 1) {
4212bfe3f2eSlogwang 			ret = rte_kvargs_process(kvlist,
4222bfe3f2eSlogwang 					PMD_FAILSAFE_HOTPLUG_POLL_KVARG,
423*d30ea906Sjfb8856606 					&fs_get_u64_arg, &failsafe_hotplug_poll);
4242bfe3f2eSlogwang 			if (ret < 0)
4252bfe3f2eSlogwang 				goto free_kvlist;
4262bfe3f2eSlogwang 		}
4272bfe3f2eSlogwang 		/* MAC addr */
4282bfe3f2eSlogwang 		arg_count = rte_kvargs_count(kvlist,
4292bfe3f2eSlogwang 				PMD_FAILSAFE_MAC_KVARG);
4302bfe3f2eSlogwang 		if (arg_count > 0) {
4312bfe3f2eSlogwang 			ret = rte_kvargs_process(kvlist,
4322bfe3f2eSlogwang 					PMD_FAILSAFE_MAC_KVARG,
4332bfe3f2eSlogwang 					&fs_get_mac_addr_arg,
4342bfe3f2eSlogwang 					&dev->data->mac_addrs[0]);
4352bfe3f2eSlogwang 			if (ret < 0)
4362bfe3f2eSlogwang 				goto free_kvlist;
4372bfe3f2eSlogwang 
438*d30ea906Sjfb8856606 			failsafe_mac_from_arg = 1;
4392bfe3f2eSlogwang 		}
4402bfe3f2eSlogwang 	}
4412bfe3f2eSlogwang 	PRIV(dev)->state = DEV_PARSED;
4422bfe3f2eSlogwang free_kvlist:
4432bfe3f2eSlogwang 	rte_kvargs_free(kvlist);
4442bfe3f2eSlogwang 	return ret;
4452bfe3f2eSlogwang }
4462bfe3f2eSlogwang 
4472bfe3f2eSlogwang void
4482bfe3f2eSlogwang failsafe_args_free(struct rte_eth_dev *dev)
4492bfe3f2eSlogwang {
4502bfe3f2eSlogwang 	struct sub_device *sdev;
4512bfe3f2eSlogwang 	uint8_t i;
4522bfe3f2eSlogwang 
4532bfe3f2eSlogwang 	FOREACH_SUBDEV(sdev, i, dev) {
4542bfe3f2eSlogwang 		free(sdev->cmdline);
4552bfe3f2eSlogwang 		sdev->cmdline = NULL;
456*d30ea906Sjfb8856606 		free(sdev->fd_str);
457*d30ea906Sjfb8856606 		sdev->fd_str = NULL;
4582bfe3f2eSlogwang 		free(sdev->devargs.args);
4592bfe3f2eSlogwang 		sdev->devargs.args = NULL;
4602bfe3f2eSlogwang 	}
4612bfe3f2eSlogwang }
4622bfe3f2eSlogwang 
4632bfe3f2eSlogwang static int
4642bfe3f2eSlogwang fs_count_device(struct rte_eth_dev *dev, const char *param,
4652bfe3f2eSlogwang 		uint8_t head __rte_unused)
4662bfe3f2eSlogwang {
4672bfe3f2eSlogwang 	size_t b = 0;
4682bfe3f2eSlogwang 
4692bfe3f2eSlogwang 	while  (param[b] != '(' &&
4702bfe3f2eSlogwang 		param[b] != '\0')
4712bfe3f2eSlogwang 		b++;
4722bfe3f2eSlogwang 	if (strncmp(param, "dev", b) != 0 &&
473*d30ea906Sjfb8856606 	    strncmp(param, "exec", b) != 0 &&
474*d30ea906Sjfb8856606 	    strncmp(param, "fd(", b) != 0) {
4752bfe3f2eSlogwang 		ERROR("Unrecognized device type: %.*s", (int)b, param);
4762bfe3f2eSlogwang 		return -EINVAL;
4772bfe3f2eSlogwang 	}
4782bfe3f2eSlogwang 	PRIV(dev)->subs_tail += 1;
4792bfe3f2eSlogwang 	return 0;
4802bfe3f2eSlogwang }
4812bfe3f2eSlogwang 
4822bfe3f2eSlogwang int
4832bfe3f2eSlogwang failsafe_args_count_subdevice(struct rte_eth_dev *dev,
4842bfe3f2eSlogwang 			const char *params)
4852bfe3f2eSlogwang {
4862bfe3f2eSlogwang 	return fs_parse_sub_devices(fs_count_device,
4872bfe3f2eSlogwang 				    dev, params);
4882bfe3f2eSlogwang }
4892bfe3f2eSlogwang 
4902bfe3f2eSlogwang static int
4912bfe3f2eSlogwang fs_parse_sub_device(struct sub_device *sdev)
4922bfe3f2eSlogwang {
4932bfe3f2eSlogwang 	struct rte_devargs *da;
4942bfe3f2eSlogwang 	char devstr[DEVARGS_MAXLEN] = "";
4952bfe3f2eSlogwang 
4962bfe3f2eSlogwang 	da = &sdev->devargs;
4972bfe3f2eSlogwang 	snprintf(devstr, sizeof(devstr), "%s,%s", da->name, da->args);
4982bfe3f2eSlogwang 	return fs_parse_device(sdev, devstr);
4992bfe3f2eSlogwang }
5002bfe3f2eSlogwang 
5012bfe3f2eSlogwang int
5022bfe3f2eSlogwang failsafe_args_parse_subs(struct rte_eth_dev *dev)
5032bfe3f2eSlogwang {
5042bfe3f2eSlogwang 	struct sub_device *sdev;
5052bfe3f2eSlogwang 	uint8_t i;
5062bfe3f2eSlogwang 	int ret = 0;
5072bfe3f2eSlogwang 
5082bfe3f2eSlogwang 	FOREACH_SUBDEV(sdev, i, dev) {
5092bfe3f2eSlogwang 		if (sdev->state >= DEV_PARSED)
5102bfe3f2eSlogwang 			continue;
5112bfe3f2eSlogwang 		if (sdev->cmdline)
5122bfe3f2eSlogwang 			ret = fs_execute_cmd(sdev, sdev->cmdline);
513*d30ea906Sjfb8856606 		else if (sdev->fd_str)
514*d30ea906Sjfb8856606 			ret = fs_read_fd(sdev, sdev->fd_str);
5152bfe3f2eSlogwang 		else
5162bfe3f2eSlogwang 			ret = fs_parse_sub_device(sdev);
5172bfe3f2eSlogwang 		if (ret == 0)
5182bfe3f2eSlogwang 			sdev->state = DEV_PARSED;
5192bfe3f2eSlogwang 	}
5202bfe3f2eSlogwang 	return 0;
5212bfe3f2eSlogwang }
522