xref: /dpdk/drivers/net/failsafe/failsafe_ether.c (revision f665790a5dbad7b645ff46f31d65e977324e7bfc)
1009c327cSOlivier Matz /* SPDX-License-Identifier: BSD-3-Clause
2ebea83f8SGaetan Rivet  * Copyright 2017 6WIND S.A.
35feecc57SShahaf Shuler  * Copyright 2017 Mellanox Technologies, Ltd
4ebea83f8SGaetan Rivet  */
5ebea83f8SGaetan Rivet 
6ebea83f8SGaetan Rivet #include <unistd.h>
7ebea83f8SGaetan Rivet 
8b737a1eeSGaetan Rivet #include <rte_flow.h>
9b737a1eeSGaetan Rivet #include <rte_flow_driver.h>
109dda3e33SMatan Azrad #include <rte_cycles.h>
11b737a1eeSGaetan Rivet 
12ebea83f8SGaetan Rivet #include "failsafe_private.h"
13ebea83f8SGaetan Rivet 
14b737a1eeSGaetan Rivet /** Print a message out of a flow error. */
15b737a1eeSGaetan Rivet static int
16b737a1eeSGaetan Rivet fs_flow_complain(struct rte_flow_error *error)
17b737a1eeSGaetan Rivet {
18b737a1eeSGaetan Rivet 	static const char *const errstrlist[] = {
19b737a1eeSGaetan Rivet 		[RTE_FLOW_ERROR_TYPE_NONE] = "no error",
20b737a1eeSGaetan Rivet 		[RTE_FLOW_ERROR_TYPE_UNSPECIFIED] = "cause unspecified",
21b737a1eeSGaetan Rivet 		[RTE_FLOW_ERROR_TYPE_HANDLE] = "flow rule (handle)",
22b737a1eeSGaetan Rivet 		[RTE_FLOW_ERROR_TYPE_ATTR_GROUP] = "group field",
23b737a1eeSGaetan Rivet 		[RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY] = "priority field",
24b737a1eeSGaetan Rivet 		[RTE_FLOW_ERROR_TYPE_ATTR_INGRESS] = "ingress field",
25b737a1eeSGaetan Rivet 		[RTE_FLOW_ERROR_TYPE_ATTR_EGRESS] = "egress field",
26b737a1eeSGaetan Rivet 		[RTE_FLOW_ERROR_TYPE_ATTR] = "attributes structure",
27b737a1eeSGaetan Rivet 		[RTE_FLOW_ERROR_TYPE_ITEM_NUM] = "pattern length",
28b737a1eeSGaetan Rivet 		[RTE_FLOW_ERROR_TYPE_ITEM] = "specific pattern item",
29b737a1eeSGaetan Rivet 		[RTE_FLOW_ERROR_TYPE_ACTION_NUM] = "number of actions",
30b737a1eeSGaetan Rivet 		[RTE_FLOW_ERROR_TYPE_ACTION] = "specific action",
31b737a1eeSGaetan Rivet 	};
32b737a1eeSGaetan Rivet 	const char *errstr;
33b737a1eeSGaetan Rivet 	char buf[32];
34b737a1eeSGaetan Rivet 	int err = rte_errno;
35b737a1eeSGaetan Rivet 
36b737a1eeSGaetan Rivet 	if ((unsigned int)error->type >= RTE_DIM(errstrlist) ||
37b737a1eeSGaetan Rivet 			!errstrlist[error->type])
38b737a1eeSGaetan Rivet 		errstr = "unknown type";
39b737a1eeSGaetan Rivet 	else
40b737a1eeSGaetan Rivet 		errstr = errstrlist[error->type];
41*f665790aSDavid Marchand 	ERROR("Caught error type %d (%s): %s%s",
42b737a1eeSGaetan Rivet 		error->type, errstr,
43b737a1eeSGaetan Rivet 		error->cause ? (snprintf(buf, sizeof(buf), "cause: %p, ",
44b737a1eeSGaetan Rivet 				error->cause), buf) : "",
45b737a1eeSGaetan Rivet 		error->message ? error->message : "(no stated reason)");
46b737a1eeSGaetan Rivet 	return -err;
47b737a1eeSGaetan Rivet }
48b737a1eeSGaetan Rivet 
49ebea83f8SGaetan Rivet static int
502cc52cd7SGaetan Rivet eth_dev_flow_isolate_set(struct rte_eth_dev *dev,
512cc52cd7SGaetan Rivet 			 struct sub_device *sdev)
522cc52cd7SGaetan Rivet {
532cc52cd7SGaetan Rivet 	struct rte_flow_error ferror;
542cc52cd7SGaetan Rivet 	int ret;
552cc52cd7SGaetan Rivet 
562cc52cd7SGaetan Rivet 	if (!PRIV(dev)->flow_isolated) {
572cc52cd7SGaetan Rivet 		DEBUG("Flow isolation already disabled");
582cc52cd7SGaetan Rivet 	} else {
592cc52cd7SGaetan Rivet 		DEBUG("Enabling flow isolation");
602cc52cd7SGaetan Rivet 		ret = rte_flow_isolate(PORT_ID(sdev),
612cc52cd7SGaetan Rivet 				       PRIV(dev)->flow_isolated,
622cc52cd7SGaetan Rivet 				       &ferror);
632cc52cd7SGaetan Rivet 		if (ret) {
642cc52cd7SGaetan Rivet 			fs_flow_complain(&ferror);
652cc52cd7SGaetan Rivet 			return ret;
662cc52cd7SGaetan Rivet 		}
672cc52cd7SGaetan Rivet 	}
682cc52cd7SGaetan Rivet 	return 0;
692cc52cd7SGaetan Rivet }
702cc52cd7SGaetan Rivet 
712cc52cd7SGaetan Rivet static int
72ebea83f8SGaetan Rivet fs_eth_dev_conf_apply(struct rte_eth_dev *dev,
73ebea83f8SGaetan Rivet 		struct sub_device *sdev)
74ebea83f8SGaetan Rivet {
75ebea83f8SGaetan Rivet 	struct rte_eth_dev *edev;
76ebea83f8SGaetan Rivet 	struct rte_vlan_filter_conf *vfc1;
77ebea83f8SGaetan Rivet 	struct rte_vlan_filter_conf *vfc2;
78b737a1eeSGaetan Rivet 	struct rte_flow *flow;
79b737a1eeSGaetan Rivet 	struct rte_flow_error ferror;
80ebea83f8SGaetan Rivet 	uint32_t i;
81ebea83f8SGaetan Rivet 	int ret;
82ebea83f8SGaetan Rivet 
83ebea83f8SGaetan Rivet 	edev = ETH(sdev);
84ebea83f8SGaetan Rivet 	/* RX queue setup */
85ebea83f8SGaetan Rivet 	for (i = 0; i < dev->data->nb_rx_queues; i++) {
86ebea83f8SGaetan Rivet 		struct rxq *rxq;
87ebea83f8SGaetan Rivet 
88ebea83f8SGaetan Rivet 		rxq = dev->data->rx_queues[i];
89ebea83f8SGaetan Rivet 		ret = rte_eth_rx_queue_setup(PORT_ID(sdev), i,
90ebea83f8SGaetan Rivet 				rxq->info.nb_desc, rxq->socket_id,
91ebea83f8SGaetan Rivet 				&rxq->info.conf, rxq->info.mp);
92ebea83f8SGaetan Rivet 		if (ret) {
93ebea83f8SGaetan Rivet 			ERROR("rx_queue_setup failed");
94ebea83f8SGaetan Rivet 			return ret;
95ebea83f8SGaetan Rivet 		}
96ebea83f8SGaetan Rivet 	}
97ebea83f8SGaetan Rivet 	/* TX queue setup */
98ebea83f8SGaetan Rivet 	for (i = 0; i < dev->data->nb_tx_queues; i++) {
99ebea83f8SGaetan Rivet 		struct txq *txq;
100ebea83f8SGaetan Rivet 
101ebea83f8SGaetan Rivet 		txq = dev->data->tx_queues[i];
102ebea83f8SGaetan Rivet 		ret = rte_eth_tx_queue_setup(PORT_ID(sdev), i,
103ebea83f8SGaetan Rivet 				txq->info.nb_desc, txq->socket_id,
104ebea83f8SGaetan Rivet 				&txq->info.conf);
105ebea83f8SGaetan Rivet 		if (ret) {
106ebea83f8SGaetan Rivet 			ERROR("tx_queue_setup failed");
107ebea83f8SGaetan Rivet 			return ret;
108ebea83f8SGaetan Rivet 		}
109ebea83f8SGaetan Rivet 	}
110ebea83f8SGaetan Rivet 	/* dev_link.link_status */
111ebea83f8SGaetan Rivet 	if (dev->data->dev_link.link_status !=
112ebea83f8SGaetan Rivet 	    edev->data->dev_link.link_status) {
113ebea83f8SGaetan Rivet 		DEBUG("Configuring link_status");
114ebea83f8SGaetan Rivet 		if (dev->data->dev_link.link_status)
115ebea83f8SGaetan Rivet 			ret = rte_eth_dev_set_link_up(PORT_ID(sdev));
116ebea83f8SGaetan Rivet 		else
117ebea83f8SGaetan Rivet 			ret = rte_eth_dev_set_link_down(PORT_ID(sdev));
118ebea83f8SGaetan Rivet 		if (ret) {
119ebea83f8SGaetan Rivet 			ERROR("Failed to apply link_status");
120ebea83f8SGaetan Rivet 			return ret;
121ebea83f8SGaetan Rivet 		}
122ebea83f8SGaetan Rivet 	} else {
123ebea83f8SGaetan Rivet 		DEBUG("link_status already set");
124ebea83f8SGaetan Rivet 	}
125ebea83f8SGaetan Rivet 	/* promiscuous */
126ebea83f8SGaetan Rivet 	if (dev->data->promiscuous != edev->data->promiscuous) {
127ebea83f8SGaetan Rivet 		DEBUG("Configuring promiscuous");
128ebea83f8SGaetan Rivet 		if (dev->data->promiscuous)
1296f6d5d21SIvan Ilchenko 			ret = rte_eth_promiscuous_enable(PORT_ID(sdev));
130ebea83f8SGaetan Rivet 		else
1316f6d5d21SIvan Ilchenko 			ret = rte_eth_promiscuous_disable(PORT_ID(sdev));
1326f6d5d21SIvan Ilchenko 		if (ret != 0) {
1336f6d5d21SIvan Ilchenko 			ERROR("Failed to apply promiscuous mode");
1346f6d5d21SIvan Ilchenko 			return ret;
1356f6d5d21SIvan Ilchenko 		}
136ebea83f8SGaetan Rivet 	} else {
137ebea83f8SGaetan Rivet 		DEBUG("promiscuous already set");
138ebea83f8SGaetan Rivet 	}
139ebea83f8SGaetan Rivet 	/* all_multicast */
140ebea83f8SGaetan Rivet 	if (dev->data->all_multicast != edev->data->all_multicast) {
141ebea83f8SGaetan Rivet 		DEBUG("Configuring all_multicast");
142ebea83f8SGaetan Rivet 		if (dev->data->all_multicast)
1435bc4baf0SIvan Ilchenko 			ret = rte_eth_allmulticast_enable(PORT_ID(sdev));
144ebea83f8SGaetan Rivet 		else
1455bc4baf0SIvan Ilchenko 			ret = rte_eth_allmulticast_disable(PORT_ID(sdev));
1465bc4baf0SIvan Ilchenko 		if (ret != 0) {
1475bc4baf0SIvan Ilchenko 			ERROR("Failed to apply allmulticast mode");
1485bc4baf0SIvan Ilchenko 			return ret;
1495bc4baf0SIvan Ilchenko 		}
150ebea83f8SGaetan Rivet 	} else {
151ebea83f8SGaetan Rivet 		DEBUG("all_multicast already set");
152ebea83f8SGaetan Rivet 	}
153ebea83f8SGaetan Rivet 	/* MTU */
154ebea83f8SGaetan Rivet 	if (dev->data->mtu != edev->data->mtu) {
155ebea83f8SGaetan Rivet 		DEBUG("Configuring MTU");
156ebea83f8SGaetan Rivet 		ret = rte_eth_dev_set_mtu(PORT_ID(sdev), dev->data->mtu);
157ebea83f8SGaetan Rivet 		if (ret) {
158ebea83f8SGaetan Rivet 			ERROR("Failed to apply MTU");
159ebea83f8SGaetan Rivet 			return ret;
160ebea83f8SGaetan Rivet 		}
161ebea83f8SGaetan Rivet 	} else {
162ebea83f8SGaetan Rivet 		DEBUG("MTU already set");
163ebea83f8SGaetan Rivet 	}
164ebea83f8SGaetan Rivet 	/* default MAC */
165ebea83f8SGaetan Rivet 	DEBUG("Configuring default MAC address");
166ebea83f8SGaetan Rivet 	ret = rte_eth_dev_default_mac_addr_set(PORT_ID(sdev),
167ebea83f8SGaetan Rivet 			&dev->data->mac_addrs[0]);
168ebea83f8SGaetan Rivet 	if (ret) {
169ebea83f8SGaetan Rivet 		ERROR("Setting default MAC address failed");
170ebea83f8SGaetan Rivet 		return ret;
171ebea83f8SGaetan Rivet 	}
172ebea83f8SGaetan Rivet 	/* additional MAC */
173ebea83f8SGaetan Rivet 	if (PRIV(dev)->nb_mac_addr > 1)
174ebea83f8SGaetan Rivet 		DEBUG("Configure additional MAC address%s",
175ebea83f8SGaetan Rivet 			(PRIV(dev)->nb_mac_addr > 2 ? "es" : ""));
176ebea83f8SGaetan Rivet 	for (i = 1; i < PRIV(dev)->nb_mac_addr; i++) {
1776d13ea8eSOlivier Matz 		struct rte_ether_addr *ea;
178ebea83f8SGaetan Rivet 
179ebea83f8SGaetan Rivet 		ea = &dev->data->mac_addrs[i];
180ebea83f8SGaetan Rivet 		ret = rte_eth_dev_mac_addr_add(PORT_ID(sdev), ea,
181ebea83f8SGaetan Rivet 				PRIV(dev)->mac_addr_pool[i]);
182ebea83f8SGaetan Rivet 		if (ret) {
18335b2d13fSOlivier Matz 			char ea_fmt[RTE_ETHER_ADDR_FMT_SIZE];
184ebea83f8SGaetan Rivet 
18535b2d13fSOlivier Matz 			rte_ether_format_addr(ea_fmt,
18635b2d13fSOlivier Matz 					RTE_ETHER_ADDR_FMT_SIZE, ea);
187ebea83f8SGaetan Rivet 			ERROR("Adding MAC address %s failed", ea_fmt);
188ae7cb246SMatan Azrad 			return ret;
189ebea83f8SGaetan Rivet 		}
190ebea83f8SGaetan Rivet 	}
191901efc0dSEvgeny Im 	/*
192901efc0dSEvgeny Im 	 * Propagate multicast MAC addresses to sub-devices,
193901efc0dSEvgeny Im 	 * if non zero number of addresses is set.
194901efc0dSEvgeny Im 	 * The condition is required to avoid breakage of failsafe
195901efc0dSEvgeny Im 	 * for sub-devices which do not support the operation
196901efc0dSEvgeny Im 	 * if the feature is really not used.
197901efc0dSEvgeny Im 	 */
198901efc0dSEvgeny Im 	if (PRIV(dev)->nb_mcast_addr > 0) {
199901efc0dSEvgeny Im 		DEBUG("Configuring multicast MAC addresses");
200901efc0dSEvgeny Im 		ret = rte_eth_dev_set_mc_addr_list(PORT_ID(sdev),
201901efc0dSEvgeny Im 						   PRIV(dev)->mcast_addrs,
202901efc0dSEvgeny Im 						   PRIV(dev)->nb_mcast_addr);
203901efc0dSEvgeny Im 		if (ret) {
204901efc0dSEvgeny Im 			ERROR("Failed to apply multicast MAC addresses");
205901efc0dSEvgeny Im 			return ret;
206901efc0dSEvgeny Im 		}
207901efc0dSEvgeny Im 	}
208ebea83f8SGaetan Rivet 	/* VLAN filter */
209ebea83f8SGaetan Rivet 	vfc1 = &dev->data->vlan_filter_conf;
210ebea83f8SGaetan Rivet 	vfc2 = &edev->data->vlan_filter_conf;
211ebea83f8SGaetan Rivet 	if (memcmp(vfc1, vfc2, sizeof(struct rte_vlan_filter_conf))) {
212ebea83f8SGaetan Rivet 		uint64_t vbit;
213ebea83f8SGaetan Rivet 		uint64_t ids;
214ebea83f8SGaetan Rivet 		size_t i;
215ebea83f8SGaetan Rivet 		uint16_t vlan_id;
216ebea83f8SGaetan Rivet 
217ebea83f8SGaetan Rivet 		DEBUG("Configuring VLAN filter");
218ebea83f8SGaetan Rivet 		for (i = 0; i < RTE_DIM(vfc1->ids); i++) {
219ebea83f8SGaetan Rivet 			if (vfc1->ids[i] == 0)
220ebea83f8SGaetan Rivet 				continue;
221ebea83f8SGaetan Rivet 			ids = vfc1->ids[i];
222ebea83f8SGaetan Rivet 			while (ids) {
223ebea83f8SGaetan Rivet 				vlan_id = 64 * i;
224ebea83f8SGaetan Rivet 				/* count trailing zeroes */
225ebea83f8SGaetan Rivet 				vbit = ~ids & (ids - 1);
226ebea83f8SGaetan Rivet 				/* clear least significant bit set */
227ebea83f8SGaetan Rivet 				ids ^= (ids ^ (ids - 1)) ^ vbit;
228ebea83f8SGaetan Rivet 				for (; vbit; vlan_id++)
229ebea83f8SGaetan Rivet 					vbit >>= 1;
230ebea83f8SGaetan Rivet 				ret = rte_eth_dev_vlan_filter(
231ebea83f8SGaetan Rivet 					PORT_ID(sdev), vlan_id, 1);
232ebea83f8SGaetan Rivet 				if (ret) {
233ebea83f8SGaetan Rivet 					ERROR("Failed to apply VLAN filter %hu",
234ebea83f8SGaetan Rivet 						vlan_id);
235ebea83f8SGaetan Rivet 					return ret;
236ebea83f8SGaetan Rivet 				}
237ebea83f8SGaetan Rivet 			}
238ebea83f8SGaetan Rivet 		}
239ebea83f8SGaetan Rivet 	} else {
240ebea83f8SGaetan Rivet 		DEBUG("VLAN filter already set");
241ebea83f8SGaetan Rivet 	}
242b737a1eeSGaetan Rivet 	/* rte_flow */
243b737a1eeSGaetan Rivet 	if (TAILQ_EMPTY(&PRIV(dev)->flow_list)) {
244b737a1eeSGaetan Rivet 		DEBUG("rte_flow already set");
245b737a1eeSGaetan Rivet 	} else {
246b737a1eeSGaetan Rivet 		DEBUG("Resetting rte_flow configuration");
247b737a1eeSGaetan Rivet 		ret = rte_flow_flush(PORT_ID(sdev), &ferror);
248b737a1eeSGaetan Rivet 		if (ret) {
249b737a1eeSGaetan Rivet 			fs_flow_complain(&ferror);
250b737a1eeSGaetan Rivet 			return ret;
251b737a1eeSGaetan Rivet 		}
252b737a1eeSGaetan Rivet 		i = 0;
253b737a1eeSGaetan Rivet 		rte_errno = 0;
254b737a1eeSGaetan Rivet 		DEBUG("Configuring rte_flow");
255b737a1eeSGaetan Rivet 		TAILQ_FOREACH(flow, &PRIV(dev)->flow_list, next) {
256b737a1eeSGaetan Rivet 			DEBUG("Creating flow #%" PRIu32, i++);
257b737a1eeSGaetan Rivet 			flow->flows[SUB_ID(sdev)] =
258b737a1eeSGaetan Rivet 				rte_flow_create(PORT_ID(sdev),
25933fcf207SAdrien Mazarguil 						flow->rule.attr,
26033fcf207SAdrien Mazarguil 						flow->rule.pattern,
26133fcf207SAdrien Mazarguil 						flow->rule.actions,
262b737a1eeSGaetan Rivet 						&ferror);
263b737a1eeSGaetan Rivet 			ret = rte_errno;
264b737a1eeSGaetan Rivet 			if (ret)
265b737a1eeSGaetan Rivet 				break;
266b737a1eeSGaetan Rivet 		}
267b737a1eeSGaetan Rivet 		if (ret) {
268b737a1eeSGaetan Rivet 			fs_flow_complain(&ferror);
269b737a1eeSGaetan Rivet 			return ret;
270b737a1eeSGaetan Rivet 		}
271b737a1eeSGaetan Rivet 	}
272ebea83f8SGaetan Rivet 	return 0;
273ebea83f8SGaetan Rivet }
274ebea83f8SGaetan Rivet 
275598fb8aeSGaetan Rivet static void
276598fb8aeSGaetan Rivet fs_dev_remove(struct sub_device *sdev)
277598fb8aeSGaetan Rivet {
278598fb8aeSGaetan Rivet 	int ret;
279598fb8aeSGaetan Rivet 
280598fb8aeSGaetan Rivet 	if (sdev == NULL)
281598fb8aeSGaetan Rivet 		return;
282598fb8aeSGaetan Rivet 	switch (sdev->state) {
283598fb8aeSGaetan Rivet 	case DEV_STARTED:
284f234e5bdSMoti Haimovsky 		failsafe_rx_intr_uninstall_subdevice(sdev);
28597742c7bSIvan Ilchenko 		ret = rte_eth_dev_stop(PORT_ID(sdev));
28697742c7bSIvan Ilchenko 		if (ret < 0)
28797742c7bSIvan Ilchenko 			ERROR("Failed to stop sub-device %u", SUB_ID(sdev));
288598fb8aeSGaetan Rivet 		sdev->state = DEV_ACTIVE;
289598fb8aeSGaetan Rivet 		/* fallthrough */
290598fb8aeSGaetan Rivet 	case DEV_ACTIVE:
2910545c580SMatan Azrad 		failsafe_eth_dev_unregister_callbacks(sdev);
2928a5a0aadSThomas Monjalon 		ret = rte_eth_dev_close(PORT_ID(sdev));
2938a5a0aadSThomas Monjalon 		if (ret < 0) {
2948a5a0aadSThomas Monjalon 			ERROR("Port close failed for sub-device %u",
2958a5a0aadSThomas Monjalon 			      PORT_ID(sdev));
2968a5a0aadSThomas Monjalon 		}
297598fb8aeSGaetan Rivet 		sdev->state = DEV_PROBED;
298598fb8aeSGaetan Rivet 		/* fallthrough */
299598fb8aeSGaetan Rivet 	case DEV_PROBED:
300911462ebSThomas Monjalon 		ret = rte_dev_remove(sdev->dev);
30175b66decSIlya Maximets 		if (ret < 0) {
302598fb8aeSGaetan Rivet 			ERROR("Bus detach failed for sub_device %u",
303598fb8aeSGaetan Rivet 			      SUB_ID(sdev));
304598fb8aeSGaetan Rivet 		} else {
305fac0ae54SMatan Azrad 			rte_eth_dev_release_port(ETH(sdev));
306598fb8aeSGaetan Rivet 		}
307598fb8aeSGaetan Rivet 		sdev->state = DEV_PARSED;
308598fb8aeSGaetan Rivet 		/* fallthrough */
309598fb8aeSGaetan Rivet 	case DEV_PARSED:
310598fb8aeSGaetan Rivet 	case DEV_UNDEFINED:
311598fb8aeSGaetan Rivet 		sdev->state = DEV_UNDEFINED;
3122f4b9983SRaslan Darawsheh 		sdev->sdev_port_id = RTE_MAX_ETHPORTS;
313598fb8aeSGaetan Rivet 		/* the end */
314598fb8aeSGaetan Rivet 		break;
315598fb8aeSGaetan Rivet 	}
31682bae1eaSMatan Azrad 	sdev->remove = 0;
317fa7bb47aSRaslan Darawsheh 	failsafe_hotplug_alarm_install(fs_dev(sdev));
318598fb8aeSGaetan Rivet }
319598fb8aeSGaetan Rivet 
3206265ab51SMatan Azrad static void
3216265ab51SMatan Azrad fs_dev_stats_save(struct sub_device *sdev)
3226265ab51SMatan Azrad {
323321809bbSMatan Azrad 	struct rte_eth_stats stats;
324321809bbSMatan Azrad 	int err;
325321809bbSMatan Azrad 
326321809bbSMatan Azrad 	/* Attempt to read current stats. */
327321809bbSMatan Azrad 	err = rte_eth_stats_get(PORT_ID(sdev), &stats);
3289dda3e33SMatan Azrad 	if (err) {
3299dda3e33SMatan Azrad 		uint64_t timestamp = sdev->stats_snapshot.timestamp;
3309dda3e33SMatan Azrad 
33109c00246SStephen Hemminger 		WARN("Could not access latest statistics from sub-device %d.",
3329dda3e33SMatan Azrad 			 SUB_ID(sdev));
3339dda3e33SMatan Azrad 		if (timestamp != 0)
33409c00246SStephen Hemminger 			WARN("Using latest snapshot taken before %"PRIu64" seconds.",
3359dda3e33SMatan Azrad 				 (rte_rdtsc() - timestamp) / rte_get_tsc_hz());
3369dda3e33SMatan Azrad 	}
337fa7bb47aSRaslan Darawsheh 	failsafe_stats_increment
338fa7bb47aSRaslan Darawsheh 		(&PRIV(fs_dev(sdev))->stats_accumulator,
3399dda3e33SMatan Azrad 		err ? &sdev->stats_snapshot.stats : &stats);
3409dda3e33SMatan Azrad 	memset(&sdev->stats_snapshot, 0, sizeof(sdev->stats_snapshot));
3416265ab51SMatan Azrad }
3426265ab51SMatan Azrad 
343598fb8aeSGaetan Rivet static inline int
344598fb8aeSGaetan Rivet fs_rxtx_clean(struct sub_device *sdev)
345598fb8aeSGaetan Rivet {
346598fb8aeSGaetan Rivet 	uint16_t i;
347598fb8aeSGaetan Rivet 
348598fb8aeSGaetan Rivet 	for (i = 0; i < ETH(sdev)->data->nb_rx_queues; i++)
349598fb8aeSGaetan Rivet 		if (FS_ATOMIC_RX(sdev, i))
350598fb8aeSGaetan Rivet 			return 0;
351598fb8aeSGaetan Rivet 	for (i = 0; i < ETH(sdev)->data->nb_tx_queues; i++)
352598fb8aeSGaetan Rivet 		if (FS_ATOMIC_TX(sdev, i))
353598fb8aeSGaetan Rivet 			return 0;
354598fb8aeSGaetan Rivet 	return 1;
355598fb8aeSGaetan Rivet }
356598fb8aeSGaetan Rivet 
357598fb8aeSGaetan Rivet void
3580545c580SMatan Azrad failsafe_eth_dev_unregister_callbacks(struct sub_device *sdev)
3590545c580SMatan Azrad {
3600545c580SMatan Azrad 	int ret;
3610545c580SMatan Azrad 
3620545c580SMatan Azrad 	if (sdev == NULL)
3630545c580SMatan Azrad 		return;
3640545c580SMatan Azrad 	if (sdev->rmv_callback) {
3650545c580SMatan Azrad 		ret = rte_eth_dev_callback_unregister(PORT_ID(sdev),
3660545c580SMatan Azrad 						RTE_ETH_EVENT_INTR_RMV,
3670545c580SMatan Azrad 						failsafe_eth_rmv_event_callback,
3680545c580SMatan Azrad 						sdev);
3690545c580SMatan Azrad 		if (ret)
3700545c580SMatan Azrad 			WARN("Failed to unregister RMV callback for sub_device"
3710545c580SMatan Azrad 			     " %d", SUB_ID(sdev));
3720545c580SMatan Azrad 		sdev->rmv_callback = 0;
3730545c580SMatan Azrad 	}
3740545c580SMatan Azrad 	if (sdev->lsc_callback) {
3750545c580SMatan Azrad 		ret = rte_eth_dev_callback_unregister(PORT_ID(sdev),
3760545c580SMatan Azrad 						RTE_ETH_EVENT_INTR_LSC,
3770545c580SMatan Azrad 						failsafe_eth_lsc_event_callback,
3780545c580SMatan Azrad 						sdev);
3790545c580SMatan Azrad 		if (ret)
3800545c580SMatan Azrad 			WARN("Failed to unregister LSC callback for sub_device"
3810545c580SMatan Azrad 			     " %d", SUB_ID(sdev));
3820545c580SMatan Azrad 		sdev->lsc_callback = 0;
3830545c580SMatan Azrad 	}
3840545c580SMatan Azrad }
3850545c580SMatan Azrad 
3860545c580SMatan Azrad void
387598fb8aeSGaetan Rivet failsafe_dev_remove(struct rte_eth_dev *dev)
388598fb8aeSGaetan Rivet {
389598fb8aeSGaetan Rivet 	struct sub_device *sdev;
390598fb8aeSGaetan Rivet 	uint8_t i;
391598fb8aeSGaetan Rivet 
392352074b3SGaetan Rivet 	FOREACH_SUBDEV(sdev, i, dev) {
393352074b3SGaetan Rivet 		if (!sdev->remove)
394352074b3SGaetan Rivet 			continue;
395352074b3SGaetan Rivet 
396352074b3SGaetan Rivet 		/* Active devices must have finished their burst and
397352074b3SGaetan Rivet 		 * their stats must be saved.
398352074b3SGaetan Rivet 		 */
399352074b3SGaetan Rivet 		if (sdev->state >= DEV_ACTIVE &&
400352074b3SGaetan Rivet 		    fs_rxtx_clean(sdev) == 0)
401352074b3SGaetan Rivet 			continue;
402655fcd68SMatan Azrad 		if (fs_lock(dev, 1) != 0)
403655fcd68SMatan Azrad 			return;
404352074b3SGaetan Rivet 		if (sdev->state >= DEV_ACTIVE)
4056265ab51SMatan Azrad 			fs_dev_stats_save(sdev);
406598fb8aeSGaetan Rivet 		fs_dev_remove(sdev);
407655fcd68SMatan Azrad 		fs_unlock(dev, 1);
408598fb8aeSGaetan Rivet 	}
4096265ab51SMatan Azrad }
410598fb8aeSGaetan Rivet 
4113db7001eSIan Dolzhansky static int
4123db7001eSIan Dolzhansky failsafe_eth_dev_rx_queues_sync(struct rte_eth_dev *dev)
4133db7001eSIan Dolzhansky {
4143db7001eSIan Dolzhansky 	struct rxq *rxq;
4153db7001eSIan Dolzhansky 	int ret;
4163db7001eSIan Dolzhansky 	uint16_t i;
4173db7001eSIan Dolzhansky 
4183db7001eSIan Dolzhansky 	for (i = 0; i < dev->data->nb_rx_queues; i++) {
4193db7001eSIan Dolzhansky 		rxq = dev->data->rx_queues[i];
4203db7001eSIan Dolzhansky 
4213db7001eSIan Dolzhansky 		if (rxq->info.conf.rx_deferred_start &&
4223db7001eSIan Dolzhansky 		    dev->data->rx_queue_state[i] ==
4233db7001eSIan Dolzhansky 						RTE_ETH_QUEUE_STATE_STARTED) {
4243db7001eSIan Dolzhansky 			/*
4253db7001eSIan Dolzhansky 			 * The subdevice Rx queue does not launch on device
4263db7001eSIan Dolzhansky 			 * start if deferred start flag is set. It needs to be
4273db7001eSIan Dolzhansky 			 * started manually in case an appropriate failsafe Rx
4283db7001eSIan Dolzhansky 			 * queue has been started earlier.
4293db7001eSIan Dolzhansky 			 */
4303db7001eSIan Dolzhansky 			ret = dev->dev_ops->rx_queue_start(dev, i);
4313db7001eSIan Dolzhansky 			if (ret) {
4323db7001eSIan Dolzhansky 				ERROR("Could not synchronize Rx queue %d", i);
4333db7001eSIan Dolzhansky 				return ret;
4343db7001eSIan Dolzhansky 			}
4353db7001eSIan Dolzhansky 		} else if (dev->data->rx_queue_state[i] ==
4363db7001eSIan Dolzhansky 						RTE_ETH_QUEUE_STATE_STOPPED) {
4373db7001eSIan Dolzhansky 			/*
4383db7001eSIan Dolzhansky 			 * The subdevice Rx queue needs to be stopped manually
4393db7001eSIan Dolzhansky 			 * in case an appropriate failsafe Rx queue has been
4403db7001eSIan Dolzhansky 			 * stopped earlier.
4413db7001eSIan Dolzhansky 			 */
4423db7001eSIan Dolzhansky 			ret = dev->dev_ops->rx_queue_stop(dev, i);
4433db7001eSIan Dolzhansky 			if (ret) {
4443db7001eSIan Dolzhansky 				ERROR("Could not synchronize Rx queue %d", i);
4453db7001eSIan Dolzhansky 				return ret;
4463db7001eSIan Dolzhansky 			}
4473db7001eSIan Dolzhansky 		}
4483db7001eSIan Dolzhansky 	}
4493db7001eSIan Dolzhansky 	return 0;
4503db7001eSIan Dolzhansky }
4513db7001eSIan Dolzhansky 
452b32c9075SIan Dolzhansky static int
453b32c9075SIan Dolzhansky failsafe_eth_dev_tx_queues_sync(struct rte_eth_dev *dev)
454b32c9075SIan Dolzhansky {
455b32c9075SIan Dolzhansky 	struct txq *txq;
456b32c9075SIan Dolzhansky 	int ret;
457b32c9075SIan Dolzhansky 	uint16_t i;
458b32c9075SIan Dolzhansky 
459b32c9075SIan Dolzhansky 	for (i = 0; i < dev->data->nb_tx_queues; i++) {
460b32c9075SIan Dolzhansky 		txq = dev->data->tx_queues[i];
461b32c9075SIan Dolzhansky 
462b32c9075SIan Dolzhansky 		if (txq->info.conf.tx_deferred_start &&
463b32c9075SIan Dolzhansky 		    dev->data->tx_queue_state[i] ==
464b32c9075SIan Dolzhansky 						RTE_ETH_QUEUE_STATE_STARTED) {
465b32c9075SIan Dolzhansky 			/*
466b32c9075SIan Dolzhansky 			 * The subdevice Tx queue does not launch on device
467b32c9075SIan Dolzhansky 			 * start if deferred start flag is set. It needs to be
468b32c9075SIan Dolzhansky 			 * started manually in case an appropriate failsafe Tx
469b32c9075SIan Dolzhansky 			 * queue has been started earlier.
470b32c9075SIan Dolzhansky 			 */
471b32c9075SIan Dolzhansky 			ret = dev->dev_ops->tx_queue_start(dev, i);
472b32c9075SIan Dolzhansky 			if (ret) {
473b32c9075SIan Dolzhansky 				ERROR("Could not synchronize Tx queue %d", i);
474b32c9075SIan Dolzhansky 				return ret;
475b32c9075SIan Dolzhansky 			}
476b32c9075SIan Dolzhansky 		} else if (dev->data->tx_queue_state[i] ==
477b32c9075SIan Dolzhansky 						RTE_ETH_QUEUE_STATE_STOPPED) {
478b32c9075SIan Dolzhansky 			/*
479b32c9075SIan Dolzhansky 			 * The subdevice Tx queue needs to be stopped manually
480b32c9075SIan Dolzhansky 			 * in case an appropriate failsafe Tx queue has been
481b32c9075SIan Dolzhansky 			 * stopped earlier.
482b32c9075SIan Dolzhansky 			 */
483b32c9075SIan Dolzhansky 			ret = dev->dev_ops->tx_queue_stop(dev, i);
484b32c9075SIan Dolzhansky 			if (ret) {
485b32c9075SIan Dolzhansky 				ERROR("Could not synchronize Tx queue %d", i);
486b32c9075SIan Dolzhansky 				return ret;
487b32c9075SIan Dolzhansky 			}
488b32c9075SIan Dolzhansky 		}
489b32c9075SIan Dolzhansky 	}
490b32c9075SIan Dolzhansky 	return 0;
491b32c9075SIan Dolzhansky }
492b32c9075SIan Dolzhansky 
493ebea83f8SGaetan Rivet int
494ebea83f8SGaetan Rivet failsafe_eth_dev_state_sync(struct rte_eth_dev *dev)
495ebea83f8SGaetan Rivet {
496ebea83f8SGaetan Rivet 	struct sub_device *sdev;
497ebea83f8SGaetan Rivet 	uint32_t inactive;
498ebea83f8SGaetan Rivet 	int ret;
499ebea83f8SGaetan Rivet 	uint8_t i;
500ebea83f8SGaetan Rivet 
501a0194d82SGaetan Rivet 	if (PRIV(dev)->state < DEV_PARSED)
502a0194d82SGaetan Rivet 		return 0;
503a0194d82SGaetan Rivet 
504a0194d82SGaetan Rivet 	ret = failsafe_args_parse_subs(dev);
505a0194d82SGaetan Rivet 	if (ret)
506598fb8aeSGaetan Rivet 		goto err_remove;
507a0194d82SGaetan Rivet 
508ebea83f8SGaetan Rivet 	if (PRIV(dev)->state < DEV_PROBED)
509ebea83f8SGaetan Rivet 		return 0;
510ebea83f8SGaetan Rivet 	ret = failsafe_eal_init(dev);
511ebea83f8SGaetan Rivet 	if (ret)
512598fb8aeSGaetan Rivet 		goto err_remove;
513ebea83f8SGaetan Rivet 	if (PRIV(dev)->state < DEV_ACTIVE)
514ebea83f8SGaetan Rivet 		return 0;
515ebea83f8SGaetan Rivet 	inactive = 0;
5162cc52cd7SGaetan Rivet 	FOREACH_SUBDEV(sdev, i, dev) {
5172cc52cd7SGaetan Rivet 		if (sdev->state == DEV_PROBED) {
518ebea83f8SGaetan Rivet 			inactive |= UINT32_C(1) << i;
5192cc52cd7SGaetan Rivet 			ret = eth_dev_flow_isolate_set(dev, sdev);
5202cc52cd7SGaetan Rivet 			if (ret) {
5212cc52cd7SGaetan Rivet 				ERROR("Could not apply configuration to sub_device %d",
5222cc52cd7SGaetan Rivet 				      i);
5232cc52cd7SGaetan Rivet 				goto err_remove;
5242cc52cd7SGaetan Rivet 			}
5252cc52cd7SGaetan Rivet 		}
5262cc52cd7SGaetan Rivet 	}
527ebea83f8SGaetan Rivet 	ret = dev->dev_ops->dev_configure(dev);
528ebea83f8SGaetan Rivet 	if (ret)
529598fb8aeSGaetan Rivet 		goto err_remove;
530ebea83f8SGaetan Rivet 	FOREACH_SUBDEV(sdev, i, dev) {
531ebea83f8SGaetan Rivet 		if (inactive & (UINT32_C(1) << i)) {
532ebea83f8SGaetan Rivet 			ret = fs_eth_dev_conf_apply(dev, sdev);
533ebea83f8SGaetan Rivet 			if (ret) {
534ebea83f8SGaetan Rivet 				ERROR("Could not apply configuration to sub_device %d",
535ebea83f8SGaetan Rivet 				      i);
536598fb8aeSGaetan Rivet 				goto err_remove;
537ebea83f8SGaetan Rivet 			}
538ebea83f8SGaetan Rivet 		}
539ebea83f8SGaetan Rivet 	}
540ebea83f8SGaetan Rivet 	/*
541ebea83f8SGaetan Rivet 	 * If new devices have been configured, check if
542ebea83f8SGaetan Rivet 	 * the link state has changed.
543ebea83f8SGaetan Rivet 	 */
544ebea83f8SGaetan Rivet 	if (inactive)
545ebea83f8SGaetan Rivet 		dev->dev_ops->link_update(dev, 1);
546ebea83f8SGaetan Rivet 	if (PRIV(dev)->state < DEV_STARTED)
547ebea83f8SGaetan Rivet 		return 0;
548ebea83f8SGaetan Rivet 	ret = dev->dev_ops->dev_start(dev);
549ebea83f8SGaetan Rivet 	if (ret)
550598fb8aeSGaetan Rivet 		goto err_remove;
5513db7001eSIan Dolzhansky 	ret = failsafe_eth_dev_rx_queues_sync(dev);
5523db7001eSIan Dolzhansky 	if (ret)
5533db7001eSIan Dolzhansky 		goto err_remove;
554b32c9075SIan Dolzhansky 	ret = failsafe_eth_dev_tx_queues_sync(dev);
555b32c9075SIan Dolzhansky 	if (ret)
556b32c9075SIan Dolzhansky 		goto err_remove;
557598fb8aeSGaetan Rivet 	return 0;
558598fb8aeSGaetan Rivet err_remove:
559598fb8aeSGaetan Rivet 	FOREACH_SUBDEV(sdev, i, dev)
560598fb8aeSGaetan Rivet 		if (sdev->state != PRIV(dev)->state)
561598fb8aeSGaetan Rivet 			sdev->remove = 1;
562ebea83f8SGaetan Rivet 	return ret;
563598fb8aeSGaetan Rivet }
564598fb8aeSGaetan Rivet 
5656265ab51SMatan Azrad void
5666265ab51SMatan Azrad failsafe_stats_increment(struct rte_eth_stats *to, struct rte_eth_stats *from)
5676265ab51SMatan Azrad {
5686265ab51SMatan Azrad 	uint32_t i;
5696265ab51SMatan Azrad 
5706265ab51SMatan Azrad 	RTE_ASSERT(to != NULL && from != NULL);
5716265ab51SMatan Azrad 	to->ipackets += from->ipackets;
5726265ab51SMatan Azrad 	to->opackets += from->opackets;
5736265ab51SMatan Azrad 	to->ibytes += from->ibytes;
5746265ab51SMatan Azrad 	to->obytes += from->obytes;
5756265ab51SMatan Azrad 	to->imissed += from->imissed;
5766265ab51SMatan Azrad 	to->ierrors += from->ierrors;
5776265ab51SMatan Azrad 	to->oerrors += from->oerrors;
5786265ab51SMatan Azrad 	to->rx_nombuf += from->rx_nombuf;
5796265ab51SMatan Azrad 	for (i = 0; i < RTE_ETHDEV_QUEUE_STAT_CNTRS; i++) {
5806265ab51SMatan Azrad 		to->q_ipackets[i] += from->q_ipackets[i];
5816265ab51SMatan Azrad 		to->q_opackets[i] += from->q_opackets[i];
5826265ab51SMatan Azrad 		to->q_ibytes[i] += from->q_ibytes[i];
5836265ab51SMatan Azrad 		to->q_obytes[i] += from->q_obytes[i];
5846265ab51SMatan Azrad 		to->q_errors[i] += from->q_errors[i];
5856265ab51SMatan Azrad 	}
5866265ab51SMatan Azrad }
5876265ab51SMatan Azrad 
588598fb8aeSGaetan Rivet int
589f8244c63SZhiyong Yang failsafe_eth_rmv_event_callback(uint16_t port_id __rte_unused,
590598fb8aeSGaetan Rivet 				enum rte_eth_event_type event __rte_unused,
591598fb8aeSGaetan Rivet 				void *cb_arg, void *out __rte_unused)
592598fb8aeSGaetan Rivet {
593598fb8aeSGaetan Rivet 	struct sub_device *sdev = cb_arg;
594598fb8aeSGaetan Rivet 
595813f085fSDavid Marchand 	if (fs_lock(fs_dev(sdev), 0) != 0)
596813f085fSDavid Marchand 		return -1;
597598fb8aeSGaetan Rivet 	/* Switch as soon as possible tx_dev. */
598fa7bb47aSRaslan Darawsheh 	fs_switch_dev(fs_dev(sdev), sdev);
599598fb8aeSGaetan Rivet 	/* Use safe bursts in any case. */
600fa7bb47aSRaslan Darawsheh 	failsafe_set_burst_fn(fs_dev(sdev), 1);
601598fb8aeSGaetan Rivet 	/*
602598fb8aeSGaetan Rivet 	 * Async removal, the sub-PMD will try to unregister
603598fb8aeSGaetan Rivet 	 * the callback at the source of the current thread context.
604598fb8aeSGaetan Rivet 	 */
605598fb8aeSGaetan Rivet 	sdev->remove = 1;
606fa7bb47aSRaslan Darawsheh 	fs_unlock(fs_dev(sdev), 0);
607ebea83f8SGaetan Rivet 	return 0;
608ebea83f8SGaetan Rivet }
609ad7d6a35SGaetan Rivet 
610ad7d6a35SGaetan Rivet int
611f8244c63SZhiyong Yang failsafe_eth_lsc_event_callback(uint16_t port_id __rte_unused,
612ad7d6a35SGaetan Rivet 				enum rte_eth_event_type event __rte_unused,
613ad7d6a35SGaetan Rivet 				void *cb_arg, void *out __rte_unused)
614ad7d6a35SGaetan Rivet {
615ad7d6a35SGaetan Rivet 	struct rte_eth_dev *dev = cb_arg;
616ad7d6a35SGaetan Rivet 	int ret;
617ad7d6a35SGaetan Rivet 
618ad7d6a35SGaetan Rivet 	ret = dev->dev_ops->link_update(dev, 0);
619ad7d6a35SGaetan Rivet 	/* We must pass on the LSC event */
620ad7d6a35SGaetan Rivet 	if (ret)
6215723fbedSFerruh Yigit 		return rte_eth_dev_callback_process(dev,
622ad7d6a35SGaetan Rivet 						    RTE_ETH_EVENT_INTR_LSC,
623cebe3d7bSThomas Monjalon 						    NULL);
624ad7d6a35SGaetan Rivet 	else
625ad7d6a35SGaetan Rivet 		return 0;
626ad7d6a35SGaetan Rivet }
6277fda13d3SMatan Azrad 
6287fda13d3SMatan Azrad /* Take sub-device ownership before it becomes exposed to the application. */
6297fda13d3SMatan Azrad int
6307fda13d3SMatan Azrad failsafe_eth_new_event_callback(uint16_t port_id,
6317fda13d3SMatan Azrad 				enum rte_eth_event_type event __rte_unused,
6327fda13d3SMatan Azrad 				void *cb_arg, void *out __rte_unused)
6337fda13d3SMatan Azrad {
6347fda13d3SMatan Azrad 	struct rte_eth_dev *fs_dev = cb_arg;
6357fda13d3SMatan Azrad 	struct sub_device *sdev;
6367fda13d3SMatan Azrad 	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
6377fda13d3SMatan Azrad 	uint8_t i;
6387fda13d3SMatan Azrad 
6397fda13d3SMatan Azrad 	FOREACH_SUBDEV_STATE(sdev, i, fs_dev, DEV_PARSED) {
6407fda13d3SMatan Azrad 		if (sdev->state >= DEV_PROBED)
6417fda13d3SMatan Azrad 			continue;
64294a6f2deSGaetan Rivet 		if (dev->device == NULL) {
643*f665790aSDavid Marchand 			WARN("Trying to probe malformed device %s.",
64494a6f2deSGaetan Rivet 			     sdev->devargs.name);
64594a6f2deSGaetan Rivet 			continue;
64694a6f2deSGaetan Rivet 		}
6477fda13d3SMatan Azrad 		if (strcmp(sdev->devargs.name, dev->device->name) != 0)
6487fda13d3SMatan Azrad 			continue;
6497fda13d3SMatan Azrad 		rte_eth_dev_owner_set(port_id, &PRIV(fs_dev)->my_owner);
6507fda13d3SMatan Azrad 		/* The actual owner will be checked after the port probing. */
6517fda13d3SMatan Azrad 		break;
6527fda13d3SMatan Azrad 	}
6537fda13d3SMatan Azrad 	return 0;
6547fda13d3SMatan Azrad }
655