xref: /linux-6.15/net/switchdev/switchdev.c (revision 558d51fa)
1007f790cSJiri Pirko /*
2007f790cSJiri Pirko  * net/switchdev/switchdev.c - Switch device API
3007f790cSJiri Pirko  * Copyright (c) 2014 Jiri Pirko <[email protected]>
4f8f21471SScott Feldman  * Copyright (c) 2014-2015 Scott Feldman <[email protected]>
5007f790cSJiri Pirko  *
6007f790cSJiri Pirko  * This program is free software; you can redistribute it and/or modify
7007f790cSJiri Pirko  * it under the terms of the GNU General Public License as published by
8007f790cSJiri Pirko  * the Free Software Foundation; either version 2 of the License, or
9007f790cSJiri Pirko  * (at your option) any later version.
10007f790cSJiri Pirko  */
11007f790cSJiri Pirko 
12007f790cSJiri Pirko #include <linux/kernel.h>
13007f790cSJiri Pirko #include <linux/types.h>
14007f790cSJiri Pirko #include <linux/init.h>
1503bf0c28SJiri Pirko #include <linux/mutex.h>
1603bf0c28SJiri Pirko #include <linux/notifier.h>
17007f790cSJiri Pirko #include <linux/netdevice.h>
185e8d9049SScott Feldman #include <net/ip_fib.h>
19007f790cSJiri Pirko #include <net/switchdev.h>
20007f790cSJiri Pirko 
21007f790cSJiri Pirko /**
22007f790cSJiri Pirko  *	netdev_switch_parent_id_get - Get ID of a switch
23007f790cSJiri Pirko  *	@dev: port device
24007f790cSJiri Pirko  *	@psid: switch ID
25007f790cSJiri Pirko  *
26007f790cSJiri Pirko  *	Get ID of a switch this port is part of.
27007f790cSJiri Pirko  */
28007f790cSJiri Pirko int netdev_switch_parent_id_get(struct net_device *dev,
29007f790cSJiri Pirko 				struct netdev_phys_item_id *psid)
30007f790cSJiri Pirko {
3198237d43SScott Feldman 	const struct swdev_ops *ops = dev->swdev_ops;
32007f790cSJiri Pirko 
3398237d43SScott Feldman 	if (!ops || !ops->swdev_parent_id_get)
34007f790cSJiri Pirko 		return -EOPNOTSUPP;
3598237d43SScott Feldman 	return ops->swdev_parent_id_get(dev, psid);
36007f790cSJiri Pirko }
37f4427bc3SJiri Pirko EXPORT_SYMBOL_GPL(netdev_switch_parent_id_get);
3838dcf357SScott Feldman 
3938dcf357SScott Feldman /**
4038dcf357SScott Feldman  *	netdev_switch_port_stp_update - Notify switch device port of STP
4138dcf357SScott Feldman  *					state change
4238dcf357SScott Feldman  *	@dev: port device
4338dcf357SScott Feldman  *	@state: port STP state
4438dcf357SScott Feldman  *
4538dcf357SScott Feldman  *	Notify switch device port of bridge port STP state change.
4638dcf357SScott Feldman  */
4738dcf357SScott Feldman int netdev_switch_port_stp_update(struct net_device *dev, u8 state)
4838dcf357SScott Feldman {
4998237d43SScott Feldman 	const struct swdev_ops *ops = dev->swdev_ops;
50*558d51faSRoopa Prabhu 	struct net_device *lower_dev;
51*558d51faSRoopa Prabhu 	struct list_head *iter;
52*558d51faSRoopa Prabhu 	int err = -EOPNOTSUPP;
5338dcf357SScott Feldman 
54*558d51faSRoopa Prabhu 	if (ops && ops->swdev_port_stp_update)
5598237d43SScott Feldman 		return ops->swdev_port_stp_update(dev, state);
56*558d51faSRoopa Prabhu 
57*558d51faSRoopa Prabhu 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
58*558d51faSRoopa Prabhu 		err = netdev_switch_port_stp_update(lower_dev, state);
59*558d51faSRoopa Prabhu 		if (err && err != -EOPNOTSUPP)
60*558d51faSRoopa Prabhu 			return err;
61*558d51faSRoopa Prabhu 	}
62*558d51faSRoopa Prabhu 
63*558d51faSRoopa Prabhu 	return err;
6438dcf357SScott Feldman }
65f4427bc3SJiri Pirko EXPORT_SYMBOL_GPL(netdev_switch_port_stp_update);
6603bf0c28SJiri Pirko 
6703bf0c28SJiri Pirko static DEFINE_MUTEX(netdev_switch_mutex);
6803bf0c28SJiri Pirko static RAW_NOTIFIER_HEAD(netdev_switch_notif_chain);
6903bf0c28SJiri Pirko 
7003bf0c28SJiri Pirko /**
71ac70c05bSSimon Horman  *	register_netdev_switch_notifier - Register notifier
7203bf0c28SJiri Pirko  *	@nb: notifier_block
7303bf0c28SJiri Pirko  *
7403bf0c28SJiri Pirko  *	Register switch device notifier. This should be used by code
7503bf0c28SJiri Pirko  *	which needs to monitor events happening in particular device.
7603bf0c28SJiri Pirko  *	Return values are same as for atomic_notifier_chain_register().
7703bf0c28SJiri Pirko  */
7803bf0c28SJiri Pirko int register_netdev_switch_notifier(struct notifier_block *nb)
7903bf0c28SJiri Pirko {
8003bf0c28SJiri Pirko 	int err;
8103bf0c28SJiri Pirko 
8203bf0c28SJiri Pirko 	mutex_lock(&netdev_switch_mutex);
8303bf0c28SJiri Pirko 	err = raw_notifier_chain_register(&netdev_switch_notif_chain, nb);
8403bf0c28SJiri Pirko 	mutex_unlock(&netdev_switch_mutex);
8503bf0c28SJiri Pirko 	return err;
8603bf0c28SJiri Pirko }
87f4427bc3SJiri Pirko EXPORT_SYMBOL_GPL(register_netdev_switch_notifier);
8803bf0c28SJiri Pirko 
8903bf0c28SJiri Pirko /**
90ac70c05bSSimon Horman  *	unregister_netdev_switch_notifier - Unregister notifier
9103bf0c28SJiri Pirko  *	@nb: notifier_block
9203bf0c28SJiri Pirko  *
9303bf0c28SJiri Pirko  *	Unregister switch device notifier.
9403bf0c28SJiri Pirko  *	Return values are same as for atomic_notifier_chain_unregister().
9503bf0c28SJiri Pirko  */
9603bf0c28SJiri Pirko int unregister_netdev_switch_notifier(struct notifier_block *nb)
9703bf0c28SJiri Pirko {
9803bf0c28SJiri Pirko 	int err;
9903bf0c28SJiri Pirko 
10003bf0c28SJiri Pirko 	mutex_lock(&netdev_switch_mutex);
10103bf0c28SJiri Pirko 	err = raw_notifier_chain_unregister(&netdev_switch_notif_chain, nb);
10203bf0c28SJiri Pirko 	mutex_unlock(&netdev_switch_mutex);
10303bf0c28SJiri Pirko 	return err;
10403bf0c28SJiri Pirko }
105f4427bc3SJiri Pirko EXPORT_SYMBOL_GPL(unregister_netdev_switch_notifier);
10603bf0c28SJiri Pirko 
10703bf0c28SJiri Pirko /**
108ac70c05bSSimon Horman  *	call_netdev_switch_notifiers - Call notifiers
10903bf0c28SJiri Pirko  *	@val: value passed unmodified to notifier function
11003bf0c28SJiri Pirko  *	@dev: port device
11103bf0c28SJiri Pirko  *	@info: notifier information data
11203bf0c28SJiri Pirko  *
11303bf0c28SJiri Pirko  *	Call all network notifier blocks. This should be called by driver
11403bf0c28SJiri Pirko  *	when it needs to propagate hardware event.
11503bf0c28SJiri Pirko  *	Return values are same as for atomic_notifier_call_chain().
11603bf0c28SJiri Pirko  */
11703bf0c28SJiri Pirko int call_netdev_switch_notifiers(unsigned long val, struct net_device *dev,
11803bf0c28SJiri Pirko 				 struct netdev_switch_notifier_info *info)
11903bf0c28SJiri Pirko {
12003bf0c28SJiri Pirko 	int err;
12103bf0c28SJiri Pirko 
12203bf0c28SJiri Pirko 	info->dev = dev;
12303bf0c28SJiri Pirko 	mutex_lock(&netdev_switch_mutex);
12403bf0c28SJiri Pirko 	err = raw_notifier_call_chain(&netdev_switch_notif_chain, val, info);
12503bf0c28SJiri Pirko 	mutex_unlock(&netdev_switch_mutex);
12603bf0c28SJiri Pirko 	return err;
12703bf0c28SJiri Pirko }
128f4427bc3SJiri Pirko EXPORT_SYMBOL_GPL(call_netdev_switch_notifiers);
1298a44dbb2SRoopa Prabhu 
1308a44dbb2SRoopa Prabhu /**
1318a44dbb2SRoopa Prabhu  *	netdev_switch_port_bridge_setlink - Notify switch device port of bridge
1328a44dbb2SRoopa Prabhu  *	port attributes
1338a44dbb2SRoopa Prabhu  *
1348a44dbb2SRoopa Prabhu  *	@dev: port device
1358a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
1368a44dbb2SRoopa Prabhu  *	@flags: bridge setlink flags
1378a44dbb2SRoopa Prabhu  *
1388a44dbb2SRoopa Prabhu  *	Notify switch device port of bridge port attributes
1398a44dbb2SRoopa Prabhu  */
1408a44dbb2SRoopa Prabhu int netdev_switch_port_bridge_setlink(struct net_device *dev,
1418a44dbb2SRoopa Prabhu 				      struct nlmsghdr *nlh, u16 flags)
1428a44dbb2SRoopa Prabhu {
1438a44dbb2SRoopa Prabhu 	const struct net_device_ops *ops = dev->netdev_ops;
1448a44dbb2SRoopa Prabhu 
1458a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
1468a44dbb2SRoopa Prabhu 		return 0;
1478a44dbb2SRoopa Prabhu 
1488a44dbb2SRoopa Prabhu 	if (!ops->ndo_bridge_setlink)
1498a44dbb2SRoopa Prabhu 		return -EOPNOTSUPP;
1508a44dbb2SRoopa Prabhu 
1518a44dbb2SRoopa Prabhu 	return ops->ndo_bridge_setlink(dev, nlh, flags);
1528a44dbb2SRoopa Prabhu }
153f4427bc3SJiri Pirko EXPORT_SYMBOL_GPL(netdev_switch_port_bridge_setlink);
1548a44dbb2SRoopa Prabhu 
1558a44dbb2SRoopa Prabhu /**
1568a44dbb2SRoopa Prabhu  *	netdev_switch_port_bridge_dellink - Notify switch device port of bridge
1578a44dbb2SRoopa Prabhu  *	port attribute delete
1588a44dbb2SRoopa Prabhu  *
1598a44dbb2SRoopa Prabhu  *	@dev: port device
1608a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
1618a44dbb2SRoopa Prabhu  *	@flags: bridge setlink flags
1628a44dbb2SRoopa Prabhu  *
1638a44dbb2SRoopa Prabhu  *	Notify switch device port of bridge port attribute delete
1648a44dbb2SRoopa Prabhu  */
1658a44dbb2SRoopa Prabhu int netdev_switch_port_bridge_dellink(struct net_device *dev,
1668a44dbb2SRoopa Prabhu 				      struct nlmsghdr *nlh, u16 flags)
1678a44dbb2SRoopa Prabhu {
1688a44dbb2SRoopa Prabhu 	const struct net_device_ops *ops = dev->netdev_ops;
1698a44dbb2SRoopa Prabhu 
1708a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
1718a44dbb2SRoopa Prabhu 		return 0;
1728a44dbb2SRoopa Prabhu 
1738a44dbb2SRoopa Prabhu 	if (!ops->ndo_bridge_dellink)
1748a44dbb2SRoopa Prabhu 		return -EOPNOTSUPP;
1758a44dbb2SRoopa Prabhu 
1768a44dbb2SRoopa Prabhu 	return ops->ndo_bridge_dellink(dev, nlh, flags);
1778a44dbb2SRoopa Prabhu }
178f4427bc3SJiri Pirko EXPORT_SYMBOL_GPL(netdev_switch_port_bridge_dellink);
1798a44dbb2SRoopa Prabhu 
1808a44dbb2SRoopa Prabhu /**
1818a44dbb2SRoopa Prabhu  *	ndo_dflt_netdev_switch_port_bridge_setlink - default ndo bridge setlink
1828a44dbb2SRoopa Prabhu  *						     op for master devices
1838a44dbb2SRoopa Prabhu  *
1848a44dbb2SRoopa Prabhu  *	@dev: port device
1858a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
1868a44dbb2SRoopa Prabhu  *	@flags: bridge setlink flags
1878a44dbb2SRoopa Prabhu  *
1888a44dbb2SRoopa Prabhu  *	Notify master device slaves of bridge port attributes
1898a44dbb2SRoopa Prabhu  */
1908a44dbb2SRoopa Prabhu int ndo_dflt_netdev_switch_port_bridge_setlink(struct net_device *dev,
1918a44dbb2SRoopa Prabhu 					       struct nlmsghdr *nlh, u16 flags)
1928a44dbb2SRoopa Prabhu {
1938a44dbb2SRoopa Prabhu 	struct net_device *lower_dev;
1948a44dbb2SRoopa Prabhu 	struct list_head *iter;
1958a44dbb2SRoopa Prabhu 	int ret = 0, err = 0;
1968a44dbb2SRoopa Prabhu 
1978a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
1988a44dbb2SRoopa Prabhu 		return ret;
1998a44dbb2SRoopa Prabhu 
2008a44dbb2SRoopa Prabhu 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
2018a44dbb2SRoopa Prabhu 		err = netdev_switch_port_bridge_setlink(lower_dev, nlh, flags);
2028a44dbb2SRoopa Prabhu 		if (err && err != -EOPNOTSUPP)
2038a44dbb2SRoopa Prabhu 			ret = err;
2048a44dbb2SRoopa Prabhu 	}
2058a44dbb2SRoopa Prabhu 
2068a44dbb2SRoopa Prabhu 	return ret;
2078a44dbb2SRoopa Prabhu }
208f4427bc3SJiri Pirko EXPORT_SYMBOL_GPL(ndo_dflt_netdev_switch_port_bridge_setlink);
2098a44dbb2SRoopa Prabhu 
2108a44dbb2SRoopa Prabhu /**
2118a44dbb2SRoopa Prabhu  *	ndo_dflt_netdev_switch_port_bridge_dellink - default ndo bridge dellink
2128a44dbb2SRoopa Prabhu  *						     op for master devices
2138a44dbb2SRoopa Prabhu  *
2148a44dbb2SRoopa Prabhu  *	@dev: port device
2158a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
2168a44dbb2SRoopa Prabhu  *	@flags: bridge dellink flags
2178a44dbb2SRoopa Prabhu  *
2188a44dbb2SRoopa Prabhu  *	Notify master device slaves of bridge port attribute deletes
2198a44dbb2SRoopa Prabhu  */
2208a44dbb2SRoopa Prabhu int ndo_dflt_netdev_switch_port_bridge_dellink(struct net_device *dev,
2218a44dbb2SRoopa Prabhu 					       struct nlmsghdr *nlh, u16 flags)
2228a44dbb2SRoopa Prabhu {
2238a44dbb2SRoopa Prabhu 	struct net_device *lower_dev;
2248a44dbb2SRoopa Prabhu 	struct list_head *iter;
2258a44dbb2SRoopa Prabhu 	int ret = 0, err = 0;
2268a44dbb2SRoopa Prabhu 
2278a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
2288a44dbb2SRoopa Prabhu 		return ret;
2298a44dbb2SRoopa Prabhu 
2308a44dbb2SRoopa Prabhu 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
2318a44dbb2SRoopa Prabhu 		err = netdev_switch_port_bridge_dellink(lower_dev, nlh, flags);
2328a44dbb2SRoopa Prabhu 		if (err && err != -EOPNOTSUPP)
2338a44dbb2SRoopa Prabhu 			ret = err;
2348a44dbb2SRoopa Prabhu 	}
2358a44dbb2SRoopa Prabhu 
2368a44dbb2SRoopa Prabhu 	return ret;
2378a44dbb2SRoopa Prabhu }
238f4427bc3SJiri Pirko EXPORT_SYMBOL_GPL(ndo_dflt_netdev_switch_port_bridge_dellink);
2395e8d9049SScott Feldman 
240b5d6fbdeSScott Feldman static struct net_device *netdev_switch_get_lowest_dev(struct net_device *dev)
241b5d6fbdeSScott Feldman {
24298237d43SScott Feldman 	const struct swdev_ops *ops = dev->swdev_ops;
243b5d6fbdeSScott Feldman 	struct net_device *lower_dev;
244b5d6fbdeSScott Feldman 	struct net_device *port_dev;
245b5d6fbdeSScott Feldman 	struct list_head *iter;
246b5d6fbdeSScott Feldman 
247b5d6fbdeSScott Feldman 	/* Recusively search down until we find a sw port dev.
24898237d43SScott Feldman 	 * (A sw port dev supports swdev_parent_id_get).
249b5d6fbdeSScott Feldman 	 */
250b5d6fbdeSScott Feldman 
251b5d6fbdeSScott Feldman 	if (dev->features & NETIF_F_HW_SWITCH_OFFLOAD &&
25298237d43SScott Feldman 	    ops && ops->swdev_parent_id_get)
253b5d6fbdeSScott Feldman 		return dev;
254b5d6fbdeSScott Feldman 
255b5d6fbdeSScott Feldman 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
256b5d6fbdeSScott Feldman 		port_dev = netdev_switch_get_lowest_dev(lower_dev);
257b5d6fbdeSScott Feldman 		if (port_dev)
258b5d6fbdeSScott Feldman 			return port_dev;
259b5d6fbdeSScott Feldman 	}
260b5d6fbdeSScott Feldman 
261b5d6fbdeSScott Feldman 	return NULL;
262b5d6fbdeSScott Feldman }
263b5d6fbdeSScott Feldman 
264b5d6fbdeSScott Feldman static struct net_device *netdev_switch_get_dev_by_nhs(struct fib_info *fi)
265b5d6fbdeSScott Feldman {
266b5d6fbdeSScott Feldman 	struct netdev_phys_item_id psid;
267b5d6fbdeSScott Feldman 	struct netdev_phys_item_id prev_psid;
268b5d6fbdeSScott Feldman 	struct net_device *dev = NULL;
269b5d6fbdeSScott Feldman 	int nhsel;
270b5d6fbdeSScott Feldman 
271b5d6fbdeSScott Feldman 	/* For this route, all nexthop devs must be on the same switch. */
272b5d6fbdeSScott Feldman 
273b5d6fbdeSScott Feldman 	for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) {
274b5d6fbdeSScott Feldman 		const struct fib_nh *nh = &fi->fib_nh[nhsel];
275b5d6fbdeSScott Feldman 
276b5d6fbdeSScott Feldman 		if (!nh->nh_dev)
277b5d6fbdeSScott Feldman 			return NULL;
278b5d6fbdeSScott Feldman 
279b5d6fbdeSScott Feldman 		dev = netdev_switch_get_lowest_dev(nh->nh_dev);
280b5d6fbdeSScott Feldman 		if (!dev)
281b5d6fbdeSScott Feldman 			return NULL;
282b5d6fbdeSScott Feldman 
283b5d6fbdeSScott Feldman 		if (netdev_switch_parent_id_get(dev, &psid))
284b5d6fbdeSScott Feldman 			return NULL;
285b5d6fbdeSScott Feldman 
286b5d6fbdeSScott Feldman 		if (nhsel > 0) {
287b5d6fbdeSScott Feldman 			if (prev_psid.id_len != psid.id_len)
288b5d6fbdeSScott Feldman 				return NULL;
289b5d6fbdeSScott Feldman 			if (memcmp(prev_psid.id, psid.id, psid.id_len))
290b5d6fbdeSScott Feldman 				return NULL;
291b5d6fbdeSScott Feldman 		}
292b5d6fbdeSScott Feldman 
293b5d6fbdeSScott Feldman 		prev_psid = psid;
294b5d6fbdeSScott Feldman 	}
295b5d6fbdeSScott Feldman 
296b5d6fbdeSScott Feldman 	return dev;
297b5d6fbdeSScott Feldman }
298b5d6fbdeSScott Feldman 
2995e8d9049SScott Feldman /**
3005e8d9049SScott Feldman  *	netdev_switch_fib_ipv4_add - Add IPv4 route entry to switch
3015e8d9049SScott Feldman  *
3025e8d9049SScott Feldman  *	@dst: route's IPv4 destination address
3035e8d9049SScott Feldman  *	@dst_len: destination address length (prefix length)
3045e8d9049SScott Feldman  *	@fi: route FIB info structure
3055e8d9049SScott Feldman  *	@tos: route TOS
3065e8d9049SScott Feldman  *	@type: route type
307f8f21471SScott Feldman  *	@nlflags: netlink flags passed in (NLM_F_*)
3085e8d9049SScott Feldman  *	@tb_id: route table ID
3095e8d9049SScott Feldman  *
3105e8d9049SScott Feldman  *	Add IPv4 route entry to switch device.
3115e8d9049SScott Feldman  */
3125e8d9049SScott Feldman int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
313f8f21471SScott Feldman 			       u8 tos, u8 type, u32 nlflags, u32 tb_id)
3145e8d9049SScott Feldman {
315b5d6fbdeSScott Feldman 	struct net_device *dev;
31698237d43SScott Feldman 	const struct swdev_ops *ops;
317b5d6fbdeSScott Feldman 	int err = 0;
318b5d6fbdeSScott Feldman 
3198e05fd71SScott Feldman 	/* Don't offload route if using custom ip rules or if
3208e05fd71SScott Feldman 	 * IPv4 FIB offloading has been disabled completely.
3218e05fd71SScott Feldman 	 */
3228e05fd71SScott Feldman 
323e1315db1SScott Feldman #ifdef CONFIG_IP_MULTIPLE_TABLES
324e1315db1SScott Feldman 	if (fi->fib_net->ipv4.fib_has_custom_rules)
325e1315db1SScott Feldman 		return 0;
326e1315db1SScott Feldman #endif
327e1315db1SScott Feldman 
328e1315db1SScott Feldman 	if (fi->fib_net->ipv4.fib_offload_disabled)
329104616e7SScott Feldman 		return 0;
330104616e7SScott Feldman 
331b5d6fbdeSScott Feldman 	dev = netdev_switch_get_dev_by_nhs(fi);
332b5d6fbdeSScott Feldman 	if (!dev)
3335e8d9049SScott Feldman 		return 0;
33498237d43SScott Feldman 	ops = dev->swdev_ops;
335b5d6fbdeSScott Feldman 
33698237d43SScott Feldman 	if (ops->swdev_fib_ipv4_add) {
33798237d43SScott Feldman 		err = ops->swdev_fib_ipv4_add(dev, htonl(dst), dst_len,
338f8f21471SScott Feldman 					      fi, tos, type, nlflags,
339f8f21471SScott Feldman 					      tb_id);
340b5d6fbdeSScott Feldman 		if (!err)
341b5d6fbdeSScott Feldman 			fi->fib_flags |= RTNH_F_EXTERNAL;
342b5d6fbdeSScott Feldman 	}
343b5d6fbdeSScott Feldman 
344b5d6fbdeSScott Feldman 	return err;
3455e8d9049SScott Feldman }
346f4427bc3SJiri Pirko EXPORT_SYMBOL_GPL(netdev_switch_fib_ipv4_add);
3475e8d9049SScott Feldman 
3485e8d9049SScott Feldman /**
3495e8d9049SScott Feldman  *	netdev_switch_fib_ipv4_del - Delete IPv4 route entry from switch
3505e8d9049SScott Feldman  *
3515e8d9049SScott Feldman  *	@dst: route's IPv4 destination address
3525e8d9049SScott Feldman  *	@dst_len: destination address length (prefix length)
3535e8d9049SScott Feldman  *	@fi: route FIB info structure
3545e8d9049SScott Feldman  *	@tos: route TOS
3555e8d9049SScott Feldman  *	@type: route type
3565e8d9049SScott Feldman  *	@tb_id: route table ID
3575e8d9049SScott Feldman  *
3585e8d9049SScott Feldman  *	Delete IPv4 route entry from switch device.
3595e8d9049SScott Feldman  */
3605e8d9049SScott Feldman int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
3615e8d9049SScott Feldman 			       u8 tos, u8 type, u32 tb_id)
3625e8d9049SScott Feldman {
363b5d6fbdeSScott Feldman 	struct net_device *dev;
36498237d43SScott Feldman 	const struct swdev_ops *ops;
365b5d6fbdeSScott Feldman 	int err = 0;
366b5d6fbdeSScott Feldman 
367b5d6fbdeSScott Feldman 	if (!(fi->fib_flags & RTNH_F_EXTERNAL))
3685e8d9049SScott Feldman 		return 0;
369b5d6fbdeSScott Feldman 
370b5d6fbdeSScott Feldman 	dev = netdev_switch_get_dev_by_nhs(fi);
371b5d6fbdeSScott Feldman 	if (!dev)
372b5d6fbdeSScott Feldman 		return 0;
37398237d43SScott Feldman 	ops = dev->swdev_ops;
374b5d6fbdeSScott Feldman 
37598237d43SScott Feldman 	if (ops->swdev_fib_ipv4_del) {
37698237d43SScott Feldman 		err = ops->swdev_fib_ipv4_del(dev, htonl(dst), dst_len,
377b5d6fbdeSScott Feldman 					      fi, tos, type, tb_id);
378b5d6fbdeSScott Feldman 		if (!err)
379b5d6fbdeSScott Feldman 			fi->fib_flags &= ~RTNH_F_EXTERNAL;
380b5d6fbdeSScott Feldman 	}
381b5d6fbdeSScott Feldman 
382b5d6fbdeSScott Feldman 	return err;
3835e8d9049SScott Feldman }
384f4427bc3SJiri Pirko EXPORT_SYMBOL_GPL(netdev_switch_fib_ipv4_del);
3858e05fd71SScott Feldman 
3868e05fd71SScott Feldman /**
3878e05fd71SScott Feldman  *	netdev_switch_fib_ipv4_abort - Abort an IPv4 FIB operation
3888e05fd71SScott Feldman  *
3898e05fd71SScott Feldman  *	@fi: route FIB info structure
3908e05fd71SScott Feldman  */
3918e05fd71SScott Feldman void netdev_switch_fib_ipv4_abort(struct fib_info *fi)
3928e05fd71SScott Feldman {
3938e05fd71SScott Feldman 	/* There was a problem installing this route to the offload
3948e05fd71SScott Feldman 	 * device.  For now, until we come up with more refined
3958e05fd71SScott Feldman 	 * policy handling, abruptly end IPv4 fib offloading for
3968e05fd71SScott Feldman 	 * for entire net by flushing offload device(s) of all
3978e05fd71SScott Feldman 	 * IPv4 routes, and mark IPv4 fib offloading broken from
3988e05fd71SScott Feldman 	 * this point forward.
3998e05fd71SScott Feldman 	 */
4008e05fd71SScott Feldman 
4018e05fd71SScott Feldman 	fib_flush_external(fi->fib_net);
4028e05fd71SScott Feldman 	fi->fib_net->ipv4.fib_offload_disabled = true;
4038e05fd71SScott Feldman }
404f4427bc3SJiri Pirko EXPORT_SYMBOL_GPL(netdev_switch_fib_ipv4_abort);
405