1d30ea906Sjfb8856606 /* SPDX-License-Identifier: BSD-3-Clause
22bfe3f2eSlogwang  * Copyright 2017 6WIND S.A.
3d30ea906Sjfb8856606  * Copyright 2017 Mellanox Technologies, Ltd
42bfe3f2eSlogwang  */
52bfe3f2eSlogwang 
62bfe3f2eSlogwang #include <unistd.h>
72bfe3f2eSlogwang 
82bfe3f2eSlogwang #include <rte_flow.h>
92bfe3f2eSlogwang #include <rte_flow_driver.h>
102bfe3f2eSlogwang #include <rte_cycles.h>
112bfe3f2eSlogwang 
122bfe3f2eSlogwang #include "failsafe_private.h"
132bfe3f2eSlogwang 
142bfe3f2eSlogwang /** Print a message out of a flow error. */
152bfe3f2eSlogwang static int
fs_flow_complain(struct rte_flow_error * error)162bfe3f2eSlogwang fs_flow_complain(struct rte_flow_error *error)
172bfe3f2eSlogwang {
182bfe3f2eSlogwang 	static const char *const errstrlist[] = {
192bfe3f2eSlogwang 		[RTE_FLOW_ERROR_TYPE_NONE] = "no error",
202bfe3f2eSlogwang 		[RTE_FLOW_ERROR_TYPE_UNSPECIFIED] = "cause unspecified",
212bfe3f2eSlogwang 		[RTE_FLOW_ERROR_TYPE_HANDLE] = "flow rule (handle)",
222bfe3f2eSlogwang 		[RTE_FLOW_ERROR_TYPE_ATTR_GROUP] = "group field",
232bfe3f2eSlogwang 		[RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY] = "priority field",
242bfe3f2eSlogwang 		[RTE_FLOW_ERROR_TYPE_ATTR_INGRESS] = "ingress field",
252bfe3f2eSlogwang 		[RTE_FLOW_ERROR_TYPE_ATTR_EGRESS] = "egress field",
262bfe3f2eSlogwang 		[RTE_FLOW_ERROR_TYPE_ATTR] = "attributes structure",
272bfe3f2eSlogwang 		[RTE_FLOW_ERROR_TYPE_ITEM_NUM] = "pattern length",
282bfe3f2eSlogwang 		[RTE_FLOW_ERROR_TYPE_ITEM] = "specific pattern item",
292bfe3f2eSlogwang 		[RTE_FLOW_ERROR_TYPE_ACTION_NUM] = "number of actions",
302bfe3f2eSlogwang 		[RTE_FLOW_ERROR_TYPE_ACTION] = "specific action",
312bfe3f2eSlogwang 	};
322bfe3f2eSlogwang 	const char *errstr;
332bfe3f2eSlogwang 	char buf[32];
342bfe3f2eSlogwang 	int err = rte_errno;
352bfe3f2eSlogwang 
362bfe3f2eSlogwang 	if ((unsigned int)error->type >= RTE_DIM(errstrlist) ||
372bfe3f2eSlogwang 			!errstrlist[error->type])
382bfe3f2eSlogwang 		errstr = "unknown type";
392bfe3f2eSlogwang 	else
402bfe3f2eSlogwang 		errstr = errstrlist[error->type];
412bfe3f2eSlogwang 	ERROR("Caught error type %d (%s): %s%s\n",
422bfe3f2eSlogwang 		error->type, errstr,
432bfe3f2eSlogwang 		error->cause ? (snprintf(buf, sizeof(buf), "cause: %p, ",
442bfe3f2eSlogwang 				error->cause), buf) : "",
452bfe3f2eSlogwang 		error->message ? error->message : "(no stated reason)");
462bfe3f2eSlogwang 	return -err;
472bfe3f2eSlogwang }
482bfe3f2eSlogwang 
492bfe3f2eSlogwang static int
eth_dev_flow_isolate_set(struct rte_eth_dev * dev,struct sub_device * sdev)502bfe3f2eSlogwang eth_dev_flow_isolate_set(struct rte_eth_dev *dev,
512bfe3f2eSlogwang 			 struct sub_device *sdev)
522bfe3f2eSlogwang {
532bfe3f2eSlogwang 	struct rte_flow_error ferror;
542bfe3f2eSlogwang 	int ret;
552bfe3f2eSlogwang 
562bfe3f2eSlogwang 	if (!PRIV(dev)->flow_isolated) {
572bfe3f2eSlogwang 		DEBUG("Flow isolation already disabled");
582bfe3f2eSlogwang 	} else {
592bfe3f2eSlogwang 		DEBUG("Enabling flow isolation");
602bfe3f2eSlogwang 		ret = rte_flow_isolate(PORT_ID(sdev),
612bfe3f2eSlogwang 				       PRIV(dev)->flow_isolated,
622bfe3f2eSlogwang 				       &ferror);
632bfe3f2eSlogwang 		if (ret) {
642bfe3f2eSlogwang 			fs_flow_complain(&ferror);
652bfe3f2eSlogwang 			return ret;
662bfe3f2eSlogwang 		}
672bfe3f2eSlogwang 	}
682bfe3f2eSlogwang 	return 0;
692bfe3f2eSlogwang }
702bfe3f2eSlogwang 
712bfe3f2eSlogwang static int
fs_eth_dev_conf_apply(struct rte_eth_dev * dev,struct sub_device * sdev)722bfe3f2eSlogwang fs_eth_dev_conf_apply(struct rte_eth_dev *dev,
732bfe3f2eSlogwang 		struct sub_device *sdev)
742bfe3f2eSlogwang {
752bfe3f2eSlogwang 	struct rte_eth_dev *edev;
762bfe3f2eSlogwang 	struct rte_vlan_filter_conf *vfc1;
772bfe3f2eSlogwang 	struct rte_vlan_filter_conf *vfc2;
782bfe3f2eSlogwang 	struct rte_flow *flow;
792bfe3f2eSlogwang 	struct rte_flow_error ferror;
802bfe3f2eSlogwang 	uint32_t i;
812bfe3f2eSlogwang 	int ret;
822bfe3f2eSlogwang 
832bfe3f2eSlogwang 	edev = ETH(sdev);
842bfe3f2eSlogwang 	/* RX queue setup */
852bfe3f2eSlogwang 	for (i = 0; i < dev->data->nb_rx_queues; i++) {
862bfe3f2eSlogwang 		struct rxq *rxq;
872bfe3f2eSlogwang 
882bfe3f2eSlogwang 		rxq = dev->data->rx_queues[i];
892bfe3f2eSlogwang 		ret = rte_eth_rx_queue_setup(PORT_ID(sdev), i,
902bfe3f2eSlogwang 				rxq->info.nb_desc, rxq->socket_id,
912bfe3f2eSlogwang 				&rxq->info.conf, rxq->info.mp);
922bfe3f2eSlogwang 		if (ret) {
932bfe3f2eSlogwang 			ERROR("rx_queue_setup failed");
942bfe3f2eSlogwang 			return ret;
952bfe3f2eSlogwang 		}
962bfe3f2eSlogwang 	}
972bfe3f2eSlogwang 	/* TX queue setup */
982bfe3f2eSlogwang 	for (i = 0; i < dev->data->nb_tx_queues; i++) {
992bfe3f2eSlogwang 		struct txq *txq;
1002bfe3f2eSlogwang 
1012bfe3f2eSlogwang 		txq = dev->data->tx_queues[i];
1022bfe3f2eSlogwang 		ret = rte_eth_tx_queue_setup(PORT_ID(sdev), i,
1032bfe3f2eSlogwang 				txq->info.nb_desc, txq->socket_id,
1042bfe3f2eSlogwang 				&txq->info.conf);
1052bfe3f2eSlogwang 		if (ret) {
1062bfe3f2eSlogwang 			ERROR("tx_queue_setup failed");
1072bfe3f2eSlogwang 			return ret;
1082bfe3f2eSlogwang 		}
1092bfe3f2eSlogwang 	}
1102bfe3f2eSlogwang 	/* dev_link.link_status */
1112bfe3f2eSlogwang 	if (dev->data->dev_link.link_status !=
1122bfe3f2eSlogwang 	    edev->data->dev_link.link_status) {
1132bfe3f2eSlogwang 		DEBUG("Configuring link_status");
1142bfe3f2eSlogwang 		if (dev->data->dev_link.link_status)
1152bfe3f2eSlogwang 			ret = rte_eth_dev_set_link_up(PORT_ID(sdev));
1162bfe3f2eSlogwang 		else
1172bfe3f2eSlogwang 			ret = rte_eth_dev_set_link_down(PORT_ID(sdev));
1182bfe3f2eSlogwang 		if (ret) {
1192bfe3f2eSlogwang 			ERROR("Failed to apply link_status");
1202bfe3f2eSlogwang 			return ret;
1212bfe3f2eSlogwang 		}
1222bfe3f2eSlogwang 	} else {
1232bfe3f2eSlogwang 		DEBUG("link_status already set");
1242bfe3f2eSlogwang 	}
1252bfe3f2eSlogwang 	/* promiscuous */
1262bfe3f2eSlogwang 	if (dev->data->promiscuous != edev->data->promiscuous) {
1272bfe3f2eSlogwang 		DEBUG("Configuring promiscuous");
1282bfe3f2eSlogwang 		if (dev->data->promiscuous)
1294418919fSjohnjiang 			ret = rte_eth_promiscuous_enable(PORT_ID(sdev));
1302bfe3f2eSlogwang 		else
1314418919fSjohnjiang 			ret = rte_eth_promiscuous_disable(PORT_ID(sdev));
1324418919fSjohnjiang 		if (ret != 0) {
1334418919fSjohnjiang 			ERROR("Failed to apply promiscuous mode");
1344418919fSjohnjiang 			return ret;
1354418919fSjohnjiang 		}
1362bfe3f2eSlogwang 	} else {
1372bfe3f2eSlogwang 		DEBUG("promiscuous already set");
1382bfe3f2eSlogwang 	}
1392bfe3f2eSlogwang 	/* all_multicast */
1402bfe3f2eSlogwang 	if (dev->data->all_multicast != edev->data->all_multicast) {
1412bfe3f2eSlogwang 		DEBUG("Configuring all_multicast");
1422bfe3f2eSlogwang 		if (dev->data->all_multicast)
1434418919fSjohnjiang 			ret = rte_eth_allmulticast_enable(PORT_ID(sdev));
1442bfe3f2eSlogwang 		else
1454418919fSjohnjiang 			ret = rte_eth_allmulticast_disable(PORT_ID(sdev));
1464418919fSjohnjiang 		if (ret != 0) {
1474418919fSjohnjiang 			ERROR("Failed to apply allmulticast mode");
1484418919fSjohnjiang 			return ret;
1494418919fSjohnjiang 		}
1502bfe3f2eSlogwang 	} else {
1512bfe3f2eSlogwang 		DEBUG("all_multicast already set");
1522bfe3f2eSlogwang 	}
1532bfe3f2eSlogwang 	/* MTU */
1542bfe3f2eSlogwang 	if (dev->data->mtu != edev->data->mtu) {
1552bfe3f2eSlogwang 		DEBUG("Configuring MTU");
1562bfe3f2eSlogwang 		ret = rte_eth_dev_set_mtu(PORT_ID(sdev), dev->data->mtu);
1572bfe3f2eSlogwang 		if (ret) {
1582bfe3f2eSlogwang 			ERROR("Failed to apply MTU");
1592bfe3f2eSlogwang 			return ret;
1602bfe3f2eSlogwang 		}
1612bfe3f2eSlogwang 	} else {
1622bfe3f2eSlogwang 		DEBUG("MTU already set");
1632bfe3f2eSlogwang 	}
1642bfe3f2eSlogwang 	/* default MAC */
1652bfe3f2eSlogwang 	DEBUG("Configuring default MAC address");
1662bfe3f2eSlogwang 	ret = rte_eth_dev_default_mac_addr_set(PORT_ID(sdev),
1672bfe3f2eSlogwang 			&dev->data->mac_addrs[0]);
1682bfe3f2eSlogwang 	if (ret) {
1692bfe3f2eSlogwang 		ERROR("Setting default MAC address failed");
1702bfe3f2eSlogwang 		return ret;
1712bfe3f2eSlogwang 	}
1722bfe3f2eSlogwang 	/* additional MAC */
1732bfe3f2eSlogwang 	if (PRIV(dev)->nb_mac_addr > 1)
1742bfe3f2eSlogwang 		DEBUG("Configure additional MAC address%s",
1752bfe3f2eSlogwang 			(PRIV(dev)->nb_mac_addr > 2 ? "es" : ""));
1762bfe3f2eSlogwang 	for (i = 1; i < PRIV(dev)->nb_mac_addr; i++) {
1774418919fSjohnjiang 		struct rte_ether_addr *ea;
1782bfe3f2eSlogwang 
1792bfe3f2eSlogwang 		ea = &dev->data->mac_addrs[i];
1802bfe3f2eSlogwang 		ret = rte_eth_dev_mac_addr_add(PORT_ID(sdev), ea,
1812bfe3f2eSlogwang 				PRIV(dev)->mac_addr_pool[i]);
1822bfe3f2eSlogwang 		if (ret) {
1834418919fSjohnjiang 			char ea_fmt[RTE_ETHER_ADDR_FMT_SIZE];
1842bfe3f2eSlogwang 
1854418919fSjohnjiang 			rte_ether_format_addr(ea_fmt,
1864418919fSjohnjiang 					RTE_ETHER_ADDR_FMT_SIZE, ea);
1872bfe3f2eSlogwang 			ERROR("Adding MAC address %s failed", ea_fmt);
1882bfe3f2eSlogwang 			return ret;
1892bfe3f2eSlogwang 		}
1902bfe3f2eSlogwang 	}
191d30ea906Sjfb8856606 	/*
192d30ea906Sjfb8856606 	 * Propagate multicast MAC addresses to sub-devices,
193d30ea906Sjfb8856606 	 * if non zero number of addresses is set.
194d30ea906Sjfb8856606 	 * The condition is required to avoid breakage of failsafe
195d30ea906Sjfb8856606 	 * for sub-devices which do not support the operation
196d30ea906Sjfb8856606 	 * if the feature is really not used.
197d30ea906Sjfb8856606 	 */
198d30ea906Sjfb8856606 	if (PRIV(dev)->nb_mcast_addr > 0) {
199d30ea906Sjfb8856606 		DEBUG("Configuring multicast MAC addresses");
200d30ea906Sjfb8856606 		ret = rte_eth_dev_set_mc_addr_list(PORT_ID(sdev),
201d30ea906Sjfb8856606 						   PRIV(dev)->mcast_addrs,
202d30ea906Sjfb8856606 						   PRIV(dev)->nb_mcast_addr);
203d30ea906Sjfb8856606 		if (ret) {
204d30ea906Sjfb8856606 			ERROR("Failed to apply multicast MAC addresses");
205d30ea906Sjfb8856606 			return ret;
206d30ea906Sjfb8856606 		}
207d30ea906Sjfb8856606 	}
2082bfe3f2eSlogwang 	/* VLAN filter */
2092bfe3f2eSlogwang 	vfc1 = &dev->data->vlan_filter_conf;
2102bfe3f2eSlogwang 	vfc2 = &edev->data->vlan_filter_conf;
2112bfe3f2eSlogwang 	if (memcmp(vfc1, vfc2, sizeof(struct rte_vlan_filter_conf))) {
2122bfe3f2eSlogwang 		uint64_t vbit;
2132bfe3f2eSlogwang 		uint64_t ids;
2142bfe3f2eSlogwang 		size_t i;
2152bfe3f2eSlogwang 		uint16_t vlan_id;
2162bfe3f2eSlogwang 
2172bfe3f2eSlogwang 		DEBUG("Configuring VLAN filter");
2182bfe3f2eSlogwang 		for (i = 0; i < RTE_DIM(vfc1->ids); i++) {
2192bfe3f2eSlogwang 			if (vfc1->ids[i] == 0)
2202bfe3f2eSlogwang 				continue;
2212bfe3f2eSlogwang 			ids = vfc1->ids[i];
2222bfe3f2eSlogwang 			while (ids) {
2232bfe3f2eSlogwang 				vlan_id = 64 * i;
2242bfe3f2eSlogwang 				/* count trailing zeroes */
2252bfe3f2eSlogwang 				vbit = ~ids & (ids - 1);
2262bfe3f2eSlogwang 				/* clear least significant bit set */
2272bfe3f2eSlogwang 				ids ^= (ids ^ (ids - 1)) ^ vbit;
2282bfe3f2eSlogwang 				for (; vbit; vlan_id++)
2292bfe3f2eSlogwang 					vbit >>= 1;
2302bfe3f2eSlogwang 				ret = rte_eth_dev_vlan_filter(
2312bfe3f2eSlogwang 					PORT_ID(sdev), vlan_id, 1);
2322bfe3f2eSlogwang 				if (ret) {
2332bfe3f2eSlogwang 					ERROR("Failed to apply VLAN filter %hu",
2342bfe3f2eSlogwang 						vlan_id);
2352bfe3f2eSlogwang 					return ret;
2362bfe3f2eSlogwang 				}
2372bfe3f2eSlogwang 			}
2382bfe3f2eSlogwang 		}
2392bfe3f2eSlogwang 	} else {
2402bfe3f2eSlogwang 		DEBUG("VLAN filter already set");
2412bfe3f2eSlogwang 	}
2422bfe3f2eSlogwang 	/* rte_flow */
2432bfe3f2eSlogwang 	if (TAILQ_EMPTY(&PRIV(dev)->flow_list)) {
2442bfe3f2eSlogwang 		DEBUG("rte_flow already set");
2452bfe3f2eSlogwang 	} else {
2462bfe3f2eSlogwang 		DEBUG("Resetting rte_flow configuration");
2472bfe3f2eSlogwang 		ret = rte_flow_flush(PORT_ID(sdev), &ferror);
2482bfe3f2eSlogwang 		if (ret) {
2492bfe3f2eSlogwang 			fs_flow_complain(&ferror);
2502bfe3f2eSlogwang 			return ret;
2512bfe3f2eSlogwang 		}
2522bfe3f2eSlogwang 		i = 0;
2532bfe3f2eSlogwang 		rte_errno = 0;
2542bfe3f2eSlogwang 		DEBUG("Configuring rte_flow");
2552bfe3f2eSlogwang 		TAILQ_FOREACH(flow, &PRIV(dev)->flow_list, next) {
2562bfe3f2eSlogwang 			DEBUG("Creating flow #%" PRIu32, i++);
2572bfe3f2eSlogwang 			flow->flows[SUB_ID(sdev)] =
2582bfe3f2eSlogwang 				rte_flow_create(PORT_ID(sdev),
259d30ea906Sjfb8856606 						flow->rule.attr,
260d30ea906Sjfb8856606 						flow->rule.pattern,
261d30ea906Sjfb8856606 						flow->rule.actions,
2622bfe3f2eSlogwang 						&ferror);
2632bfe3f2eSlogwang 			ret = rte_errno;
2642bfe3f2eSlogwang 			if (ret)
2652bfe3f2eSlogwang 				break;
2662bfe3f2eSlogwang 		}
2672bfe3f2eSlogwang 		if (ret) {
2682bfe3f2eSlogwang 			fs_flow_complain(&ferror);
2692bfe3f2eSlogwang 			return ret;
2702bfe3f2eSlogwang 		}
2712bfe3f2eSlogwang 	}
2722bfe3f2eSlogwang 	return 0;
2732bfe3f2eSlogwang }
2742bfe3f2eSlogwang 
2752bfe3f2eSlogwang static void
fs_dev_remove(struct sub_device * sdev)2762bfe3f2eSlogwang fs_dev_remove(struct sub_device *sdev)
2772bfe3f2eSlogwang {
2782bfe3f2eSlogwang 	int ret;
2792bfe3f2eSlogwang 
2802bfe3f2eSlogwang 	if (sdev == NULL)
2812bfe3f2eSlogwang 		return;
2822bfe3f2eSlogwang 	switch (sdev->state) {
2832bfe3f2eSlogwang 	case DEV_STARTED:
284d30ea906Sjfb8856606 		failsafe_rx_intr_uninstall_subdevice(sdev);
285*2d9fd380Sjfb8856606 		ret = rte_eth_dev_stop(PORT_ID(sdev));
286*2d9fd380Sjfb8856606 		if (ret < 0)
287*2d9fd380Sjfb8856606 			ERROR("Failed to stop sub-device %u", SUB_ID(sdev));
2882bfe3f2eSlogwang 		sdev->state = DEV_ACTIVE;
2892bfe3f2eSlogwang 		/* fallthrough */
2902bfe3f2eSlogwang 	case DEV_ACTIVE:
291579bf1e2Sjfb8856606 		failsafe_eth_dev_unregister_callbacks(sdev);
292*2d9fd380Sjfb8856606 		ret = rte_eth_dev_close(PORT_ID(sdev));
293*2d9fd380Sjfb8856606 		if (ret < 0) {
294*2d9fd380Sjfb8856606 			ERROR("Port close failed for sub-device %u",
295*2d9fd380Sjfb8856606 			      PORT_ID(sdev));
296*2d9fd380Sjfb8856606 		}
2972bfe3f2eSlogwang 		sdev->state = DEV_PROBED;
2982bfe3f2eSlogwang 		/* fallthrough */
2992bfe3f2eSlogwang 	case DEV_PROBED:
300d30ea906Sjfb8856606 		ret = rte_dev_remove(sdev->dev);
3014b05018fSfengbojiang 		if (ret < 0) {
3022bfe3f2eSlogwang 			ERROR("Bus detach failed for sub_device %u",
3032bfe3f2eSlogwang 			      SUB_ID(sdev));
3042bfe3f2eSlogwang 		} else {
305d30ea906Sjfb8856606 			rte_eth_dev_release_port(ETH(sdev));
3062bfe3f2eSlogwang 		}
3072bfe3f2eSlogwang 		sdev->state = DEV_PARSED;
3082bfe3f2eSlogwang 		/* fallthrough */
3092bfe3f2eSlogwang 	case DEV_PARSED:
3102bfe3f2eSlogwang 	case DEV_UNDEFINED:
3112bfe3f2eSlogwang 		sdev->state = DEV_UNDEFINED;
3124418919fSjohnjiang 		sdev->sdev_port_id = RTE_MAX_ETHPORTS;
3132bfe3f2eSlogwang 		/* the end */
3142bfe3f2eSlogwang 		break;
3152bfe3f2eSlogwang 	}
316d30ea906Sjfb8856606 	sdev->remove = 0;
3174418919fSjohnjiang 	failsafe_hotplug_alarm_install(fs_dev(sdev));
3182bfe3f2eSlogwang }
3192bfe3f2eSlogwang 
3202bfe3f2eSlogwang static void
fs_dev_stats_save(struct sub_device * sdev)3212bfe3f2eSlogwang fs_dev_stats_save(struct sub_device *sdev)
3222bfe3f2eSlogwang {
3232bfe3f2eSlogwang 	struct rte_eth_stats stats;
3242bfe3f2eSlogwang 	int err;
3252bfe3f2eSlogwang 
3262bfe3f2eSlogwang 	/* Attempt to read current stats. */
3272bfe3f2eSlogwang 	err = rte_eth_stats_get(PORT_ID(sdev), &stats);
3282bfe3f2eSlogwang 	if (err) {
3292bfe3f2eSlogwang 		uint64_t timestamp = sdev->stats_snapshot.timestamp;
3302bfe3f2eSlogwang 
3310c6bd470Sfengbojiang 		WARN("Could not access latest statistics from sub-device %d.",
3322bfe3f2eSlogwang 			 SUB_ID(sdev));
3332bfe3f2eSlogwang 		if (timestamp != 0)
3340c6bd470Sfengbojiang 			WARN("Using latest snapshot taken before %"PRIu64" seconds.",
3352bfe3f2eSlogwang 				 (rte_rdtsc() - timestamp) / rte_get_tsc_hz());
3362bfe3f2eSlogwang 	}
3374418919fSjohnjiang 	failsafe_stats_increment
3384418919fSjohnjiang 		(&PRIV(fs_dev(sdev))->stats_accumulator,
3392bfe3f2eSlogwang 		err ? &sdev->stats_snapshot.stats : &stats);
3402bfe3f2eSlogwang 	memset(&sdev->stats_snapshot, 0, sizeof(sdev->stats_snapshot));
3412bfe3f2eSlogwang }
3422bfe3f2eSlogwang 
3432bfe3f2eSlogwang static inline int
fs_rxtx_clean(struct sub_device * sdev)3442bfe3f2eSlogwang fs_rxtx_clean(struct sub_device *sdev)
3452bfe3f2eSlogwang {
3462bfe3f2eSlogwang 	uint16_t i;
3472bfe3f2eSlogwang 
3482bfe3f2eSlogwang 	for (i = 0; i < ETH(sdev)->data->nb_rx_queues; i++)
3492bfe3f2eSlogwang 		if (FS_ATOMIC_RX(sdev, i))
3502bfe3f2eSlogwang 			return 0;
3512bfe3f2eSlogwang 	for (i = 0; i < ETH(sdev)->data->nb_tx_queues; i++)
3522bfe3f2eSlogwang 		if (FS_ATOMIC_TX(sdev, i))
3532bfe3f2eSlogwang 			return 0;
3542bfe3f2eSlogwang 	return 1;
3552bfe3f2eSlogwang }
3562bfe3f2eSlogwang 
3572bfe3f2eSlogwang void
failsafe_eth_dev_unregister_callbacks(struct sub_device * sdev)358579bf1e2Sjfb8856606 failsafe_eth_dev_unregister_callbacks(struct sub_device *sdev)
359579bf1e2Sjfb8856606 {
360579bf1e2Sjfb8856606 	int ret;
361579bf1e2Sjfb8856606 
362579bf1e2Sjfb8856606 	if (sdev == NULL)
363579bf1e2Sjfb8856606 		return;
364579bf1e2Sjfb8856606 	if (sdev->rmv_callback) {
365579bf1e2Sjfb8856606 		ret = rte_eth_dev_callback_unregister(PORT_ID(sdev),
366579bf1e2Sjfb8856606 						RTE_ETH_EVENT_INTR_RMV,
367579bf1e2Sjfb8856606 						failsafe_eth_rmv_event_callback,
368579bf1e2Sjfb8856606 						sdev);
369579bf1e2Sjfb8856606 		if (ret)
370579bf1e2Sjfb8856606 			WARN("Failed to unregister RMV callback for sub_device"
371579bf1e2Sjfb8856606 			     " %d", SUB_ID(sdev));
372579bf1e2Sjfb8856606 		sdev->rmv_callback = 0;
373579bf1e2Sjfb8856606 	}
374579bf1e2Sjfb8856606 	if (sdev->lsc_callback) {
375579bf1e2Sjfb8856606 		ret = rte_eth_dev_callback_unregister(PORT_ID(sdev),
376579bf1e2Sjfb8856606 						RTE_ETH_EVENT_INTR_LSC,
377579bf1e2Sjfb8856606 						failsafe_eth_lsc_event_callback,
378579bf1e2Sjfb8856606 						sdev);
379579bf1e2Sjfb8856606 		if (ret)
380579bf1e2Sjfb8856606 			WARN("Failed to unregister LSC callback for sub_device"
381579bf1e2Sjfb8856606 			     " %d", SUB_ID(sdev));
382579bf1e2Sjfb8856606 		sdev->lsc_callback = 0;
383579bf1e2Sjfb8856606 	}
384579bf1e2Sjfb8856606 }
385579bf1e2Sjfb8856606 
386579bf1e2Sjfb8856606 void
failsafe_dev_remove(struct rte_eth_dev * dev)3872bfe3f2eSlogwang failsafe_dev_remove(struct rte_eth_dev *dev)
3882bfe3f2eSlogwang {
3892bfe3f2eSlogwang 	struct sub_device *sdev;
3902bfe3f2eSlogwang 	uint8_t i;
3912bfe3f2eSlogwang 
3920c6bd470Sfengbojiang 	FOREACH_SUBDEV(sdev, i, dev) {
3930c6bd470Sfengbojiang 		if (!sdev->remove)
3940c6bd470Sfengbojiang 			continue;
3950c6bd470Sfengbojiang 
3960c6bd470Sfengbojiang 		/* Active devices must have finished their burst and
3970c6bd470Sfengbojiang 		 * their stats must be saved.
3980c6bd470Sfengbojiang 		 */
3990c6bd470Sfengbojiang 		if (sdev->state >= DEV_ACTIVE &&
4000c6bd470Sfengbojiang 		    fs_rxtx_clean(sdev) == 0)
4010c6bd470Sfengbojiang 			continue;
402d30ea906Sjfb8856606 		if (fs_lock(dev, 1) != 0)
403d30ea906Sjfb8856606 			return;
4040c6bd470Sfengbojiang 		if (sdev->state >= DEV_ACTIVE)
4052bfe3f2eSlogwang 			fs_dev_stats_save(sdev);
4062bfe3f2eSlogwang 		fs_dev_remove(sdev);
407d30ea906Sjfb8856606 		fs_unlock(dev, 1);
4082bfe3f2eSlogwang 	}
4092bfe3f2eSlogwang }
4102bfe3f2eSlogwang 
411d30ea906Sjfb8856606 static int
failsafe_eth_dev_rx_queues_sync(struct rte_eth_dev * dev)412d30ea906Sjfb8856606 failsafe_eth_dev_rx_queues_sync(struct rte_eth_dev *dev)
413d30ea906Sjfb8856606 {
414d30ea906Sjfb8856606 	struct rxq *rxq;
415d30ea906Sjfb8856606 	int ret;
416d30ea906Sjfb8856606 	uint16_t i;
417d30ea906Sjfb8856606 
418d30ea906Sjfb8856606 	for (i = 0; i < dev->data->nb_rx_queues; i++) {
419d30ea906Sjfb8856606 		rxq = dev->data->rx_queues[i];
420d30ea906Sjfb8856606 
421d30ea906Sjfb8856606 		if (rxq->info.conf.rx_deferred_start &&
422d30ea906Sjfb8856606 		    dev->data->rx_queue_state[i] ==
423d30ea906Sjfb8856606 						RTE_ETH_QUEUE_STATE_STARTED) {
424d30ea906Sjfb8856606 			/*
425d30ea906Sjfb8856606 			 * The subdevice Rx queue does not launch on device
426d30ea906Sjfb8856606 			 * start if deferred start flag is set. It needs to be
427d30ea906Sjfb8856606 			 * started manually in case an appropriate failsafe Rx
428d30ea906Sjfb8856606 			 * queue has been started earlier.
429d30ea906Sjfb8856606 			 */
430d30ea906Sjfb8856606 			ret = dev->dev_ops->rx_queue_start(dev, i);
431d30ea906Sjfb8856606 			if (ret) {
432d30ea906Sjfb8856606 				ERROR("Could not synchronize Rx queue %d", i);
433d30ea906Sjfb8856606 				return ret;
434d30ea906Sjfb8856606 			}
435d30ea906Sjfb8856606 		} else if (dev->data->rx_queue_state[i] ==
436d30ea906Sjfb8856606 						RTE_ETH_QUEUE_STATE_STOPPED) {
437d30ea906Sjfb8856606 			/*
438d30ea906Sjfb8856606 			 * The subdevice Rx queue needs to be stopped manually
439d30ea906Sjfb8856606 			 * in case an appropriate failsafe Rx queue has been
440d30ea906Sjfb8856606 			 * stopped earlier.
441d30ea906Sjfb8856606 			 */
442d30ea906Sjfb8856606 			ret = dev->dev_ops->rx_queue_stop(dev, i);
443d30ea906Sjfb8856606 			if (ret) {
444d30ea906Sjfb8856606 				ERROR("Could not synchronize Rx queue %d", i);
445d30ea906Sjfb8856606 				return ret;
446d30ea906Sjfb8856606 			}
447d30ea906Sjfb8856606 		}
448d30ea906Sjfb8856606 	}
449d30ea906Sjfb8856606 	return 0;
450d30ea906Sjfb8856606 }
451d30ea906Sjfb8856606 
452d30ea906Sjfb8856606 static int
failsafe_eth_dev_tx_queues_sync(struct rte_eth_dev * dev)453d30ea906Sjfb8856606 failsafe_eth_dev_tx_queues_sync(struct rte_eth_dev *dev)
454d30ea906Sjfb8856606 {
455d30ea906Sjfb8856606 	struct txq *txq;
456d30ea906Sjfb8856606 	int ret;
457d30ea906Sjfb8856606 	uint16_t i;
458d30ea906Sjfb8856606 
459d30ea906Sjfb8856606 	for (i = 0; i < dev->data->nb_tx_queues; i++) {
460d30ea906Sjfb8856606 		txq = dev->data->tx_queues[i];
461d30ea906Sjfb8856606 
462d30ea906Sjfb8856606 		if (txq->info.conf.tx_deferred_start &&
463d30ea906Sjfb8856606 		    dev->data->tx_queue_state[i] ==
464d30ea906Sjfb8856606 						RTE_ETH_QUEUE_STATE_STARTED) {
465d30ea906Sjfb8856606 			/*
466d30ea906Sjfb8856606 			 * The subdevice Tx queue does not launch on device
467d30ea906Sjfb8856606 			 * start if deferred start flag is set. It needs to be
468d30ea906Sjfb8856606 			 * started manually in case an appropriate failsafe Tx
469d30ea906Sjfb8856606 			 * queue has been started earlier.
470d30ea906Sjfb8856606 			 */
471d30ea906Sjfb8856606 			ret = dev->dev_ops->tx_queue_start(dev, i);
472d30ea906Sjfb8856606 			if (ret) {
473d30ea906Sjfb8856606 				ERROR("Could not synchronize Tx queue %d", i);
474d30ea906Sjfb8856606 				return ret;
475d30ea906Sjfb8856606 			}
476d30ea906Sjfb8856606 		} else if (dev->data->tx_queue_state[i] ==
477d30ea906Sjfb8856606 						RTE_ETH_QUEUE_STATE_STOPPED) {
478d30ea906Sjfb8856606 			/*
479d30ea906Sjfb8856606 			 * The subdevice Tx queue needs to be stopped manually
480d30ea906Sjfb8856606 			 * in case an appropriate failsafe Tx queue has been
481d30ea906Sjfb8856606 			 * stopped earlier.
482d30ea906Sjfb8856606 			 */
483d30ea906Sjfb8856606 			ret = dev->dev_ops->tx_queue_stop(dev, i);
484d30ea906Sjfb8856606 			if (ret) {
485d30ea906Sjfb8856606 				ERROR("Could not synchronize Tx queue %d", i);
486d30ea906Sjfb8856606 				return ret;
487d30ea906Sjfb8856606 			}
488d30ea906Sjfb8856606 		}
489d30ea906Sjfb8856606 	}
490d30ea906Sjfb8856606 	return 0;
491d30ea906Sjfb8856606 }
492d30ea906Sjfb8856606 
4932bfe3f2eSlogwang int
failsafe_eth_dev_state_sync(struct rte_eth_dev * dev)4942bfe3f2eSlogwang failsafe_eth_dev_state_sync(struct rte_eth_dev *dev)
4952bfe3f2eSlogwang {
4962bfe3f2eSlogwang 	struct sub_device *sdev;
4972bfe3f2eSlogwang 	uint32_t inactive;
4982bfe3f2eSlogwang 	int ret;
4992bfe3f2eSlogwang 	uint8_t i;
5002bfe3f2eSlogwang 
5012bfe3f2eSlogwang 	if (PRIV(dev)->state < DEV_PARSED)
5022bfe3f2eSlogwang 		return 0;
5032bfe3f2eSlogwang 
5042bfe3f2eSlogwang 	ret = failsafe_args_parse_subs(dev);
5052bfe3f2eSlogwang 	if (ret)
5062bfe3f2eSlogwang 		goto err_remove;
5072bfe3f2eSlogwang 
5082bfe3f2eSlogwang 	if (PRIV(dev)->state < DEV_PROBED)
5092bfe3f2eSlogwang 		return 0;
5102bfe3f2eSlogwang 	ret = failsafe_eal_init(dev);
5112bfe3f2eSlogwang 	if (ret)
5122bfe3f2eSlogwang 		goto err_remove;
5132bfe3f2eSlogwang 	if (PRIV(dev)->state < DEV_ACTIVE)
5142bfe3f2eSlogwang 		return 0;
5152bfe3f2eSlogwang 	inactive = 0;
5162bfe3f2eSlogwang 	FOREACH_SUBDEV(sdev, i, dev) {
5172bfe3f2eSlogwang 		if (sdev->state == DEV_PROBED) {
5182bfe3f2eSlogwang 			inactive |= UINT32_C(1) << i;
5192bfe3f2eSlogwang 			ret = eth_dev_flow_isolate_set(dev, sdev);
5202bfe3f2eSlogwang 			if (ret) {
5212bfe3f2eSlogwang 				ERROR("Could not apply configuration to sub_device %d",
5222bfe3f2eSlogwang 				      i);
5232bfe3f2eSlogwang 				goto err_remove;
5242bfe3f2eSlogwang 			}
5252bfe3f2eSlogwang 		}
5262bfe3f2eSlogwang 	}
5272bfe3f2eSlogwang 	ret = dev->dev_ops->dev_configure(dev);
5282bfe3f2eSlogwang 	if (ret)
5292bfe3f2eSlogwang 		goto err_remove;
5302bfe3f2eSlogwang 	FOREACH_SUBDEV(sdev, i, dev) {
5312bfe3f2eSlogwang 		if (inactive & (UINT32_C(1) << i)) {
5322bfe3f2eSlogwang 			ret = fs_eth_dev_conf_apply(dev, sdev);
5332bfe3f2eSlogwang 			if (ret) {
5342bfe3f2eSlogwang 				ERROR("Could not apply configuration to sub_device %d",
5352bfe3f2eSlogwang 				      i);
5362bfe3f2eSlogwang 				goto err_remove;
5372bfe3f2eSlogwang 			}
5382bfe3f2eSlogwang 		}
5392bfe3f2eSlogwang 	}
5402bfe3f2eSlogwang 	/*
5412bfe3f2eSlogwang 	 * If new devices have been configured, check if
5422bfe3f2eSlogwang 	 * the link state has changed.
5432bfe3f2eSlogwang 	 */
5442bfe3f2eSlogwang 	if (inactive)
5452bfe3f2eSlogwang 		dev->dev_ops->link_update(dev, 1);
5462bfe3f2eSlogwang 	if (PRIV(dev)->state < DEV_STARTED)
5472bfe3f2eSlogwang 		return 0;
5482bfe3f2eSlogwang 	ret = dev->dev_ops->dev_start(dev);
5492bfe3f2eSlogwang 	if (ret)
5502bfe3f2eSlogwang 		goto err_remove;
551d30ea906Sjfb8856606 	ret = failsafe_eth_dev_rx_queues_sync(dev);
552d30ea906Sjfb8856606 	if (ret)
553d30ea906Sjfb8856606 		goto err_remove;
554d30ea906Sjfb8856606 	ret = failsafe_eth_dev_tx_queues_sync(dev);
555d30ea906Sjfb8856606 	if (ret)
556d30ea906Sjfb8856606 		goto err_remove;
5572bfe3f2eSlogwang 	return 0;
5582bfe3f2eSlogwang err_remove:
5592bfe3f2eSlogwang 	FOREACH_SUBDEV(sdev, i, dev)
5602bfe3f2eSlogwang 		if (sdev->state != PRIV(dev)->state)
5612bfe3f2eSlogwang 			sdev->remove = 1;
5622bfe3f2eSlogwang 	return ret;
5632bfe3f2eSlogwang }
5642bfe3f2eSlogwang 
5652bfe3f2eSlogwang void
failsafe_stats_increment(struct rte_eth_stats * to,struct rte_eth_stats * from)5662bfe3f2eSlogwang failsafe_stats_increment(struct rte_eth_stats *to, struct rte_eth_stats *from)
5672bfe3f2eSlogwang {
5682bfe3f2eSlogwang 	uint32_t i;
5692bfe3f2eSlogwang 
5702bfe3f2eSlogwang 	RTE_ASSERT(to != NULL && from != NULL);
5712bfe3f2eSlogwang 	to->ipackets += from->ipackets;
5722bfe3f2eSlogwang 	to->opackets += from->opackets;
5732bfe3f2eSlogwang 	to->ibytes += from->ibytes;
5742bfe3f2eSlogwang 	to->obytes += from->obytes;
5752bfe3f2eSlogwang 	to->imissed += from->imissed;
5762bfe3f2eSlogwang 	to->ierrors += from->ierrors;
5772bfe3f2eSlogwang 	to->oerrors += from->oerrors;
5782bfe3f2eSlogwang 	to->rx_nombuf += from->rx_nombuf;
5792bfe3f2eSlogwang 	for (i = 0; i < RTE_ETHDEV_QUEUE_STAT_CNTRS; i++) {
5802bfe3f2eSlogwang 		to->q_ipackets[i] += from->q_ipackets[i];
5812bfe3f2eSlogwang 		to->q_opackets[i] += from->q_opackets[i];
5822bfe3f2eSlogwang 		to->q_ibytes[i] += from->q_ibytes[i];
5832bfe3f2eSlogwang 		to->q_obytes[i] += from->q_obytes[i];
5842bfe3f2eSlogwang 		to->q_errors[i] += from->q_errors[i];
5852bfe3f2eSlogwang 	}
5862bfe3f2eSlogwang }
5872bfe3f2eSlogwang 
5882bfe3f2eSlogwang int
failsafe_eth_rmv_event_callback(uint16_t port_id __rte_unused,enum rte_eth_event_type event __rte_unused,void * cb_arg,void * out __rte_unused)5892bfe3f2eSlogwang failsafe_eth_rmv_event_callback(uint16_t port_id __rte_unused,
5902bfe3f2eSlogwang 				enum rte_eth_event_type event __rte_unused,
5912bfe3f2eSlogwang 				void *cb_arg, void *out __rte_unused)
5922bfe3f2eSlogwang {
5932bfe3f2eSlogwang 	struct sub_device *sdev = cb_arg;
5942bfe3f2eSlogwang 
5954418919fSjohnjiang 	fs_lock(fs_dev(sdev), 0);
5962bfe3f2eSlogwang 	/* Switch as soon as possible tx_dev. */
5974418919fSjohnjiang 	fs_switch_dev(fs_dev(sdev), sdev);
5982bfe3f2eSlogwang 	/* Use safe bursts in any case. */
5994418919fSjohnjiang 	failsafe_set_burst_fn(fs_dev(sdev), 1);
6002bfe3f2eSlogwang 	/*
6012bfe3f2eSlogwang 	 * Async removal, the sub-PMD will try to unregister
6022bfe3f2eSlogwang 	 * the callback at the source of the current thread context.
6032bfe3f2eSlogwang 	 */
6042bfe3f2eSlogwang 	sdev->remove = 1;
6054418919fSjohnjiang 	fs_unlock(fs_dev(sdev), 0);
6062bfe3f2eSlogwang 	return 0;
6072bfe3f2eSlogwang }
6082bfe3f2eSlogwang 
6092bfe3f2eSlogwang int
failsafe_eth_lsc_event_callback(uint16_t port_id __rte_unused,enum rte_eth_event_type event __rte_unused,void * cb_arg,void * out __rte_unused)6102bfe3f2eSlogwang failsafe_eth_lsc_event_callback(uint16_t port_id __rte_unused,
6112bfe3f2eSlogwang 				enum rte_eth_event_type event __rte_unused,
6122bfe3f2eSlogwang 				void *cb_arg, void *out __rte_unused)
6132bfe3f2eSlogwang {
6142bfe3f2eSlogwang 	struct rte_eth_dev *dev = cb_arg;
6152bfe3f2eSlogwang 	int ret;
6162bfe3f2eSlogwang 
6172bfe3f2eSlogwang 	ret = dev->dev_ops->link_update(dev, 0);
6182bfe3f2eSlogwang 	/* We must pass on the LSC event */
6192bfe3f2eSlogwang 	if (ret)
620*2d9fd380Sjfb8856606 		return rte_eth_dev_callback_process(dev,
6212bfe3f2eSlogwang 						    RTE_ETH_EVENT_INTR_LSC,
622d30ea906Sjfb8856606 						    NULL);
6232bfe3f2eSlogwang 	else
6242bfe3f2eSlogwang 		return 0;
6252bfe3f2eSlogwang }
626d30ea906Sjfb8856606 
627d30ea906Sjfb8856606 /* Take sub-device ownership before it becomes exposed to the application. */
628d30ea906Sjfb8856606 int
failsafe_eth_new_event_callback(uint16_t port_id,enum rte_eth_event_type event __rte_unused,void * cb_arg,void * out __rte_unused)629d30ea906Sjfb8856606 failsafe_eth_new_event_callback(uint16_t port_id,
630d30ea906Sjfb8856606 				enum rte_eth_event_type event __rte_unused,
631d30ea906Sjfb8856606 				void *cb_arg, void *out __rte_unused)
632d30ea906Sjfb8856606 {
633d30ea906Sjfb8856606 	struct rte_eth_dev *fs_dev = cb_arg;
634d30ea906Sjfb8856606 	struct sub_device *sdev;
635d30ea906Sjfb8856606 	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
636d30ea906Sjfb8856606 	uint8_t i;
637d30ea906Sjfb8856606 
638d30ea906Sjfb8856606 	FOREACH_SUBDEV_STATE(sdev, i, fs_dev, DEV_PARSED) {
639d30ea906Sjfb8856606 		if (sdev->state >= DEV_PROBED)
640d30ea906Sjfb8856606 			continue;
641*2d9fd380Sjfb8856606 		if (dev->device == NULL) {
642*2d9fd380Sjfb8856606 			WARN("Trying to probe malformed device %s.\n",
643*2d9fd380Sjfb8856606 			     sdev->devargs.name);
644*2d9fd380Sjfb8856606 			continue;
645*2d9fd380Sjfb8856606 		}
646d30ea906Sjfb8856606 		if (strcmp(sdev->devargs.name, dev->device->name) != 0)
647d30ea906Sjfb8856606 			continue;
648d30ea906Sjfb8856606 		rte_eth_dev_owner_set(port_id, &PRIV(fs_dev)->my_owner);
649d30ea906Sjfb8856606 		/* The actual owner will be checked after the port probing. */
650d30ea906Sjfb8856606 		break;
651d30ea906Sjfb8856606 	}
652d30ea906Sjfb8856606 	return 0;
653d30ea906Sjfb8856606 }
654