xref: /linux-6.15/net/switchdev/switchdev.c (revision f8e20a9f)
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 /**
223094333dSScott Feldman  *	switchdev_port_attr_get - Get port attribute
233094333dSScott Feldman  *
243094333dSScott Feldman  *	@dev: port device
253094333dSScott Feldman  *	@attr: attribute to get
263094333dSScott Feldman  */
273094333dSScott Feldman int switchdev_port_attr_get(struct net_device *dev, struct switchdev_attr *attr)
283094333dSScott Feldman {
293094333dSScott Feldman 	const struct switchdev_ops *ops = dev->switchdev_ops;
303094333dSScott Feldman 	struct net_device *lower_dev;
313094333dSScott Feldman 	struct list_head *iter;
323094333dSScott Feldman 	struct switchdev_attr first = {
333094333dSScott Feldman 		.id = SWITCHDEV_ATTR_UNDEFINED
343094333dSScott Feldman 	};
353094333dSScott Feldman 	int err = -EOPNOTSUPP;
363094333dSScott Feldman 
373094333dSScott Feldman 	if (ops && ops->switchdev_port_attr_get)
383094333dSScott Feldman 		return ops->switchdev_port_attr_get(dev, attr);
393094333dSScott Feldman 
403094333dSScott Feldman 	if (attr->flags & SWITCHDEV_F_NO_RECURSE)
413094333dSScott Feldman 		return err;
423094333dSScott Feldman 
433094333dSScott Feldman 	/* Switch device port(s) may be stacked under
443094333dSScott Feldman 	 * bond/team/vlan dev, so recurse down to get attr on
453094333dSScott Feldman 	 * each port.  Return -ENODATA if attr values don't
463094333dSScott Feldman 	 * compare across ports.
473094333dSScott Feldman 	 */
483094333dSScott Feldman 
493094333dSScott Feldman 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
503094333dSScott Feldman 		err = switchdev_port_attr_get(lower_dev, attr);
513094333dSScott Feldman 		if (err)
523094333dSScott Feldman 			break;
533094333dSScott Feldman 		if (first.id == SWITCHDEV_ATTR_UNDEFINED)
543094333dSScott Feldman 			first = *attr;
553094333dSScott Feldman 		else if (memcmp(&first, attr, sizeof(*attr)))
563094333dSScott Feldman 			return -ENODATA;
573094333dSScott Feldman 	}
583094333dSScott Feldman 
593094333dSScott Feldman 	return err;
603094333dSScott Feldman }
613094333dSScott Feldman EXPORT_SYMBOL_GPL(switchdev_port_attr_get);
623094333dSScott Feldman 
633094333dSScott Feldman static int __switchdev_port_attr_set(struct net_device *dev,
643094333dSScott Feldman 				     struct switchdev_attr *attr)
653094333dSScott Feldman {
663094333dSScott Feldman 	const struct switchdev_ops *ops = dev->switchdev_ops;
673094333dSScott Feldman 	struct net_device *lower_dev;
683094333dSScott Feldman 	struct list_head *iter;
693094333dSScott Feldman 	int err = -EOPNOTSUPP;
703094333dSScott Feldman 
713094333dSScott Feldman 	if (ops && ops->switchdev_port_attr_set)
723094333dSScott Feldman 		return ops->switchdev_port_attr_set(dev, attr);
733094333dSScott Feldman 
743094333dSScott Feldman 	if (attr->flags & SWITCHDEV_F_NO_RECURSE)
753094333dSScott Feldman 		return err;
763094333dSScott Feldman 
773094333dSScott Feldman 	/* Switch device port(s) may be stacked under
783094333dSScott Feldman 	 * bond/team/vlan dev, so recurse down to set attr on
793094333dSScott Feldman 	 * each port.
803094333dSScott Feldman 	 */
813094333dSScott Feldman 
823094333dSScott Feldman 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
833094333dSScott Feldman 		err = __switchdev_port_attr_set(lower_dev, attr);
843094333dSScott Feldman 		if (err)
853094333dSScott Feldman 			break;
863094333dSScott Feldman 	}
873094333dSScott Feldman 
883094333dSScott Feldman 	return err;
893094333dSScott Feldman }
903094333dSScott Feldman 
913094333dSScott Feldman struct switchdev_attr_set_work {
923094333dSScott Feldman 	struct work_struct work;
933094333dSScott Feldman 	struct net_device *dev;
943094333dSScott Feldman 	struct switchdev_attr attr;
953094333dSScott Feldman };
963094333dSScott Feldman 
973094333dSScott Feldman static void switchdev_port_attr_set_work(struct work_struct *work)
983094333dSScott Feldman {
993094333dSScott Feldman 	struct switchdev_attr_set_work *asw =
1003094333dSScott Feldman 		container_of(work, struct switchdev_attr_set_work, work);
1013094333dSScott Feldman 	int err;
1023094333dSScott Feldman 
1033094333dSScott Feldman 	rtnl_lock();
1043094333dSScott Feldman 	err = switchdev_port_attr_set(asw->dev, &asw->attr);
1053094333dSScott Feldman 	BUG_ON(err);
1063094333dSScott Feldman 	rtnl_unlock();
1073094333dSScott Feldman 
1083094333dSScott Feldman 	dev_put(asw->dev);
1093094333dSScott Feldman 	kfree(work);
1103094333dSScott Feldman }
1113094333dSScott Feldman 
1123094333dSScott Feldman static int switchdev_port_attr_set_defer(struct net_device *dev,
1133094333dSScott Feldman 					 struct switchdev_attr *attr)
1143094333dSScott Feldman {
1153094333dSScott Feldman 	struct switchdev_attr_set_work *asw;
1163094333dSScott Feldman 
1173094333dSScott Feldman 	asw = kmalloc(sizeof(*asw), GFP_ATOMIC);
1183094333dSScott Feldman 	if (!asw)
1193094333dSScott Feldman 		return -ENOMEM;
1203094333dSScott Feldman 
1213094333dSScott Feldman 	INIT_WORK(&asw->work, switchdev_port_attr_set_work);
1223094333dSScott Feldman 
1233094333dSScott Feldman 	dev_hold(dev);
1243094333dSScott Feldman 	asw->dev = dev;
1253094333dSScott Feldman 	memcpy(&asw->attr, attr, sizeof(asw->attr));
1263094333dSScott Feldman 
1273094333dSScott Feldman 	schedule_work(&asw->work);
1283094333dSScott Feldman 
1293094333dSScott Feldman 	return 0;
1303094333dSScott Feldman }
1313094333dSScott Feldman 
1323094333dSScott Feldman /**
1333094333dSScott Feldman  *	switchdev_port_attr_set - Set port attribute
1343094333dSScott Feldman  *
1353094333dSScott Feldman  *	@dev: port device
1363094333dSScott Feldman  *	@attr: attribute to set
1373094333dSScott Feldman  *
1383094333dSScott Feldman  *	Use a 2-phase prepare-commit transaction model to ensure
1393094333dSScott Feldman  *	system is not left in a partially updated state due to
1403094333dSScott Feldman  *	failure from driver/device.
1413094333dSScott Feldman  */
1423094333dSScott Feldman int switchdev_port_attr_set(struct net_device *dev, struct switchdev_attr *attr)
1433094333dSScott Feldman {
1443094333dSScott Feldman 	int err;
1453094333dSScott Feldman 
1463094333dSScott Feldman 	if (!rtnl_is_locked()) {
1473094333dSScott Feldman 		/* Running prepare-commit transaction across stacked
1483094333dSScott Feldman 		 * devices requires nothing moves, so if rtnl_lock is
1493094333dSScott Feldman 		 * not held, schedule a worker thread to hold rtnl_lock
1503094333dSScott Feldman 		 * while setting attr.
1513094333dSScott Feldman 		 */
1523094333dSScott Feldman 
1533094333dSScott Feldman 		return switchdev_port_attr_set_defer(dev, attr);
1543094333dSScott Feldman 	}
1553094333dSScott Feldman 
1563094333dSScott Feldman 	/* Phase I: prepare for attr set. Driver/device should fail
1573094333dSScott Feldman 	 * here if there are going to be issues in the commit phase,
1583094333dSScott Feldman 	 * such as lack of resources or support.  The driver/device
1593094333dSScott Feldman 	 * should reserve resources needed for the commit phase here,
1603094333dSScott Feldman 	 * but should not commit the attr.
1613094333dSScott Feldman 	 */
1623094333dSScott Feldman 
1633094333dSScott Feldman 	attr->trans = SWITCHDEV_TRANS_PREPARE;
1643094333dSScott Feldman 	err = __switchdev_port_attr_set(dev, attr);
1653094333dSScott Feldman 	if (err) {
1663094333dSScott Feldman 		/* Prepare phase failed: abort the transaction.  Any
1673094333dSScott Feldman 		 * resources reserved in the prepare phase are
1683094333dSScott Feldman 		 * released.
1693094333dSScott Feldman 		 */
1703094333dSScott Feldman 
1713094333dSScott Feldman 		attr->trans = SWITCHDEV_TRANS_ABORT;
1723094333dSScott Feldman 		__switchdev_port_attr_set(dev, attr);
1733094333dSScott Feldman 
1743094333dSScott Feldman 		return err;
1753094333dSScott Feldman 	}
1763094333dSScott Feldman 
1773094333dSScott Feldman 	/* Phase II: commit attr set.  This cannot fail as a fault
1783094333dSScott Feldman 	 * of driver/device.  If it does, it's a bug in the driver/device
1793094333dSScott Feldman 	 * because the driver said everythings was OK in phase I.
1803094333dSScott Feldman 	 */
1813094333dSScott Feldman 
1823094333dSScott Feldman 	attr->trans = SWITCHDEV_TRANS_COMMIT;
1833094333dSScott Feldman 	err = __switchdev_port_attr_set(dev, attr);
1843094333dSScott Feldman 	BUG_ON(err);
1853094333dSScott Feldman 
1863094333dSScott Feldman 	return err;
1873094333dSScott Feldman }
1883094333dSScott Feldman EXPORT_SYMBOL_GPL(switchdev_port_attr_set);
1893094333dSScott Feldman 
1903094333dSScott Feldman /**
191ebb9a03aSJiri Pirko  *	switchdev_port_stp_update - Notify switch device port of STP
19238dcf357SScott Feldman  *					state change
19338dcf357SScott Feldman  *	@dev: port device
19438dcf357SScott Feldman  *	@state: port STP state
19538dcf357SScott Feldman  *
19638dcf357SScott Feldman  *	Notify switch device port of bridge port STP state change.
19738dcf357SScott Feldman  */
198ebb9a03aSJiri Pirko int switchdev_port_stp_update(struct net_device *dev, u8 state)
19938dcf357SScott Feldman {
2009d47c0a2SJiri Pirko 	const struct switchdev_ops *ops = dev->switchdev_ops;
201558d51faSRoopa Prabhu 	struct net_device *lower_dev;
202558d51faSRoopa Prabhu 	struct list_head *iter;
203558d51faSRoopa Prabhu 	int err = -EOPNOTSUPP;
20438dcf357SScott Feldman 
2059d47c0a2SJiri Pirko 	if (ops && ops->switchdev_port_stp_update)
2069d47c0a2SJiri Pirko 		return ops->switchdev_port_stp_update(dev, state);
207558d51faSRoopa Prabhu 
208558d51faSRoopa Prabhu 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
209ebb9a03aSJiri Pirko 		err = switchdev_port_stp_update(lower_dev, state);
210558d51faSRoopa Prabhu 		if (err && err != -EOPNOTSUPP)
211558d51faSRoopa Prabhu 			return err;
212558d51faSRoopa Prabhu 	}
213558d51faSRoopa Prabhu 
214558d51faSRoopa Prabhu 	return err;
21538dcf357SScott Feldman }
216ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_port_stp_update);
21703bf0c28SJiri Pirko 
218ebb9a03aSJiri Pirko static DEFINE_MUTEX(switchdev_mutex);
219ebb9a03aSJiri Pirko static RAW_NOTIFIER_HEAD(switchdev_notif_chain);
22003bf0c28SJiri Pirko 
22103bf0c28SJiri Pirko /**
222ebb9a03aSJiri Pirko  *	register_switchdev_notifier - Register notifier
22303bf0c28SJiri Pirko  *	@nb: notifier_block
22403bf0c28SJiri Pirko  *
22503bf0c28SJiri Pirko  *	Register switch device notifier. This should be used by code
22603bf0c28SJiri Pirko  *	which needs to monitor events happening in particular device.
22703bf0c28SJiri Pirko  *	Return values are same as for atomic_notifier_chain_register().
22803bf0c28SJiri Pirko  */
229ebb9a03aSJiri Pirko int register_switchdev_notifier(struct notifier_block *nb)
23003bf0c28SJiri Pirko {
23103bf0c28SJiri Pirko 	int err;
23203bf0c28SJiri Pirko 
233ebb9a03aSJiri Pirko 	mutex_lock(&switchdev_mutex);
234ebb9a03aSJiri Pirko 	err = raw_notifier_chain_register(&switchdev_notif_chain, nb);
235ebb9a03aSJiri Pirko 	mutex_unlock(&switchdev_mutex);
23603bf0c28SJiri Pirko 	return err;
23703bf0c28SJiri Pirko }
238ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(register_switchdev_notifier);
23903bf0c28SJiri Pirko 
24003bf0c28SJiri Pirko /**
241ebb9a03aSJiri Pirko  *	unregister_switchdev_notifier - Unregister notifier
24203bf0c28SJiri Pirko  *	@nb: notifier_block
24303bf0c28SJiri Pirko  *
24403bf0c28SJiri Pirko  *	Unregister switch device notifier.
24503bf0c28SJiri Pirko  *	Return values are same as for atomic_notifier_chain_unregister().
24603bf0c28SJiri Pirko  */
247ebb9a03aSJiri Pirko int unregister_switchdev_notifier(struct notifier_block *nb)
24803bf0c28SJiri Pirko {
24903bf0c28SJiri Pirko 	int err;
25003bf0c28SJiri Pirko 
251ebb9a03aSJiri Pirko 	mutex_lock(&switchdev_mutex);
252ebb9a03aSJiri Pirko 	err = raw_notifier_chain_unregister(&switchdev_notif_chain, nb);
253ebb9a03aSJiri Pirko 	mutex_unlock(&switchdev_mutex);
25403bf0c28SJiri Pirko 	return err;
25503bf0c28SJiri Pirko }
256ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(unregister_switchdev_notifier);
25703bf0c28SJiri Pirko 
25803bf0c28SJiri Pirko /**
259ebb9a03aSJiri Pirko  *	call_switchdev_notifiers - Call notifiers
26003bf0c28SJiri Pirko  *	@val: value passed unmodified to notifier function
26103bf0c28SJiri Pirko  *	@dev: port device
26203bf0c28SJiri Pirko  *	@info: notifier information data
26303bf0c28SJiri Pirko  *
26403bf0c28SJiri Pirko  *	Call all network notifier blocks. This should be called by driver
26503bf0c28SJiri Pirko  *	when it needs to propagate hardware event.
26603bf0c28SJiri Pirko  *	Return values are same as for atomic_notifier_call_chain().
26703bf0c28SJiri Pirko  */
268ebb9a03aSJiri Pirko int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
269ebb9a03aSJiri Pirko 			     struct switchdev_notifier_info *info)
27003bf0c28SJiri Pirko {
27103bf0c28SJiri Pirko 	int err;
27203bf0c28SJiri Pirko 
27303bf0c28SJiri Pirko 	info->dev = dev;
274ebb9a03aSJiri Pirko 	mutex_lock(&switchdev_mutex);
275ebb9a03aSJiri Pirko 	err = raw_notifier_call_chain(&switchdev_notif_chain, val, info);
276ebb9a03aSJiri Pirko 	mutex_unlock(&switchdev_mutex);
27703bf0c28SJiri Pirko 	return err;
27803bf0c28SJiri Pirko }
279ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
2808a44dbb2SRoopa Prabhu 
2818a44dbb2SRoopa Prabhu /**
282ebb9a03aSJiri Pirko  *	switchdev_port_bridge_setlink - Notify switch device port of bridge
2838a44dbb2SRoopa Prabhu  *	port attributes
2848a44dbb2SRoopa Prabhu  *
2858a44dbb2SRoopa Prabhu  *	@dev: port device
2868a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
2878a44dbb2SRoopa Prabhu  *	@flags: bridge setlink flags
2888a44dbb2SRoopa Prabhu  *
2898a44dbb2SRoopa Prabhu  *	Notify switch device port of bridge port attributes
2908a44dbb2SRoopa Prabhu  */
291ebb9a03aSJiri Pirko int switchdev_port_bridge_setlink(struct net_device *dev,
2928a44dbb2SRoopa Prabhu 				  struct nlmsghdr *nlh, u16 flags)
2938a44dbb2SRoopa Prabhu {
2948a44dbb2SRoopa Prabhu 	const struct net_device_ops *ops = dev->netdev_ops;
2958a44dbb2SRoopa Prabhu 
2968a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
2978a44dbb2SRoopa Prabhu 		return 0;
2988a44dbb2SRoopa Prabhu 
2998a44dbb2SRoopa Prabhu 	if (!ops->ndo_bridge_setlink)
3008a44dbb2SRoopa Prabhu 		return -EOPNOTSUPP;
3018a44dbb2SRoopa Prabhu 
3028a44dbb2SRoopa Prabhu 	return ops->ndo_bridge_setlink(dev, nlh, flags);
3038a44dbb2SRoopa Prabhu }
304ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_port_bridge_setlink);
3058a44dbb2SRoopa Prabhu 
3068a44dbb2SRoopa Prabhu /**
307ebb9a03aSJiri Pirko  *	switchdev_port_bridge_dellink - Notify switch device port of bridge
3088a44dbb2SRoopa Prabhu  *	port attribute delete
3098a44dbb2SRoopa Prabhu  *
3108a44dbb2SRoopa Prabhu  *	@dev: port device
3118a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
3128a44dbb2SRoopa Prabhu  *	@flags: bridge setlink flags
3138a44dbb2SRoopa Prabhu  *
3148a44dbb2SRoopa Prabhu  *	Notify switch device port of bridge port attribute delete
3158a44dbb2SRoopa Prabhu  */
316ebb9a03aSJiri Pirko int switchdev_port_bridge_dellink(struct net_device *dev,
3178a44dbb2SRoopa Prabhu 				  struct nlmsghdr *nlh, u16 flags)
3188a44dbb2SRoopa Prabhu {
3198a44dbb2SRoopa Prabhu 	const struct net_device_ops *ops = dev->netdev_ops;
3208a44dbb2SRoopa Prabhu 
3218a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
3228a44dbb2SRoopa Prabhu 		return 0;
3238a44dbb2SRoopa Prabhu 
3248a44dbb2SRoopa Prabhu 	if (!ops->ndo_bridge_dellink)
3258a44dbb2SRoopa Prabhu 		return -EOPNOTSUPP;
3268a44dbb2SRoopa Prabhu 
3278a44dbb2SRoopa Prabhu 	return ops->ndo_bridge_dellink(dev, nlh, flags);
3288a44dbb2SRoopa Prabhu }
329ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_port_bridge_dellink);
3308a44dbb2SRoopa Prabhu 
3318a44dbb2SRoopa Prabhu /**
332ebb9a03aSJiri Pirko  *	ndo_dflt_switchdev_port_bridge_setlink - default ndo bridge setlink
3338a44dbb2SRoopa Prabhu  *						 op for master devices
3348a44dbb2SRoopa Prabhu  *
3358a44dbb2SRoopa Prabhu  *	@dev: port device
3368a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
3378a44dbb2SRoopa Prabhu  *	@flags: bridge setlink flags
3388a44dbb2SRoopa Prabhu  *
3398a44dbb2SRoopa Prabhu  *	Notify master device slaves of bridge port attributes
3408a44dbb2SRoopa Prabhu  */
341ebb9a03aSJiri Pirko int ndo_dflt_switchdev_port_bridge_setlink(struct net_device *dev,
3428a44dbb2SRoopa Prabhu 					   struct nlmsghdr *nlh, u16 flags)
3438a44dbb2SRoopa Prabhu {
3448a44dbb2SRoopa Prabhu 	struct net_device *lower_dev;
3458a44dbb2SRoopa Prabhu 	struct list_head *iter;
3468a44dbb2SRoopa Prabhu 	int ret = 0, err = 0;
3478a44dbb2SRoopa Prabhu 
3488a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
3498a44dbb2SRoopa Prabhu 		return ret;
3508a44dbb2SRoopa Prabhu 
3518a44dbb2SRoopa Prabhu 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
352ebb9a03aSJiri Pirko 		err = switchdev_port_bridge_setlink(lower_dev, nlh, flags);
3538a44dbb2SRoopa Prabhu 		if (err && err != -EOPNOTSUPP)
3548a44dbb2SRoopa Prabhu 			ret = err;
3558a44dbb2SRoopa Prabhu 	}
3568a44dbb2SRoopa Prabhu 
3578a44dbb2SRoopa Prabhu 	return ret;
3588a44dbb2SRoopa Prabhu }
359ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(ndo_dflt_switchdev_port_bridge_setlink);
3608a44dbb2SRoopa Prabhu 
3618a44dbb2SRoopa Prabhu /**
362ebb9a03aSJiri Pirko  *	ndo_dflt_switchdev_port_bridge_dellink - default ndo bridge dellink
3638a44dbb2SRoopa Prabhu  *						 op for master devices
3648a44dbb2SRoopa Prabhu  *
3658a44dbb2SRoopa Prabhu  *	@dev: port device
3668a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
3678a44dbb2SRoopa Prabhu  *	@flags: bridge dellink flags
3688a44dbb2SRoopa Prabhu  *
3698a44dbb2SRoopa Prabhu  *	Notify master device slaves of bridge port attribute deletes
3708a44dbb2SRoopa Prabhu  */
371ebb9a03aSJiri Pirko int ndo_dflt_switchdev_port_bridge_dellink(struct net_device *dev,
3728a44dbb2SRoopa Prabhu 					   struct nlmsghdr *nlh, u16 flags)
3738a44dbb2SRoopa Prabhu {
3748a44dbb2SRoopa Prabhu 	struct net_device *lower_dev;
3758a44dbb2SRoopa Prabhu 	struct list_head *iter;
3768a44dbb2SRoopa Prabhu 	int ret = 0, err = 0;
3778a44dbb2SRoopa Prabhu 
3788a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
3798a44dbb2SRoopa Prabhu 		return ret;
3808a44dbb2SRoopa Prabhu 
3818a44dbb2SRoopa Prabhu 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
382ebb9a03aSJiri Pirko 		err = switchdev_port_bridge_dellink(lower_dev, nlh, flags);
3838a44dbb2SRoopa Prabhu 		if (err && err != -EOPNOTSUPP)
3848a44dbb2SRoopa Prabhu 			ret = err;
3858a44dbb2SRoopa Prabhu 	}
3868a44dbb2SRoopa Prabhu 
3878a44dbb2SRoopa Prabhu 	return ret;
3888a44dbb2SRoopa Prabhu }
389ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(ndo_dflt_switchdev_port_bridge_dellink);
3905e8d9049SScott Feldman 
391ebb9a03aSJiri Pirko static struct net_device *switchdev_get_lowest_dev(struct net_device *dev)
392b5d6fbdeSScott Feldman {
3939d47c0a2SJiri Pirko 	const struct switchdev_ops *ops = dev->switchdev_ops;
394b5d6fbdeSScott Feldman 	struct net_device *lower_dev;
395b5d6fbdeSScott Feldman 	struct net_device *port_dev;
396b5d6fbdeSScott Feldman 	struct list_head *iter;
397b5d6fbdeSScott Feldman 
398b5d6fbdeSScott Feldman 	/* Recusively search down until we find a sw port dev.
399*f8e20a9fSScott Feldman 	 * (A sw port dev supports switchdev_port_attr_get).
400b5d6fbdeSScott Feldman 	 */
401b5d6fbdeSScott Feldman 
402*f8e20a9fSScott Feldman 	if (ops && ops->switchdev_port_attr_get)
403b5d6fbdeSScott Feldman 		return dev;
404b5d6fbdeSScott Feldman 
405b5d6fbdeSScott Feldman 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
406ebb9a03aSJiri Pirko 		port_dev = switchdev_get_lowest_dev(lower_dev);
407b5d6fbdeSScott Feldman 		if (port_dev)
408b5d6fbdeSScott Feldman 			return port_dev;
409b5d6fbdeSScott Feldman 	}
410b5d6fbdeSScott Feldman 
411b5d6fbdeSScott Feldman 	return NULL;
412b5d6fbdeSScott Feldman }
413b5d6fbdeSScott Feldman 
414ebb9a03aSJiri Pirko static struct net_device *switchdev_get_dev_by_nhs(struct fib_info *fi)
415b5d6fbdeSScott Feldman {
416*f8e20a9fSScott Feldman 	struct switchdev_attr attr = {
417*f8e20a9fSScott Feldman 		.id = SWITCHDEV_ATTR_PORT_PARENT_ID,
418*f8e20a9fSScott Feldman 	};
419*f8e20a9fSScott Feldman 	struct switchdev_attr prev_attr;
420b5d6fbdeSScott Feldman 	struct net_device *dev = NULL;
421b5d6fbdeSScott Feldman 	int nhsel;
422b5d6fbdeSScott Feldman 
423b5d6fbdeSScott Feldman 	/* For this route, all nexthop devs must be on the same switch. */
424b5d6fbdeSScott Feldman 
425b5d6fbdeSScott Feldman 	for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) {
426b5d6fbdeSScott Feldman 		const struct fib_nh *nh = &fi->fib_nh[nhsel];
427b5d6fbdeSScott Feldman 
428b5d6fbdeSScott Feldman 		if (!nh->nh_dev)
429b5d6fbdeSScott Feldman 			return NULL;
430b5d6fbdeSScott Feldman 
431ebb9a03aSJiri Pirko 		dev = switchdev_get_lowest_dev(nh->nh_dev);
432b5d6fbdeSScott Feldman 		if (!dev)
433b5d6fbdeSScott Feldman 			return NULL;
434b5d6fbdeSScott Feldman 
435*f8e20a9fSScott Feldman 		if (switchdev_port_attr_get(dev, &attr))
436b5d6fbdeSScott Feldman 			return NULL;
437b5d6fbdeSScott Feldman 
438b5d6fbdeSScott Feldman 		if (nhsel > 0) {
439*f8e20a9fSScott Feldman 			if (prev_attr.ppid.id_len != attr.ppid.id_len)
440b5d6fbdeSScott Feldman 				return NULL;
441*f8e20a9fSScott Feldman 			if (memcmp(prev_attr.ppid.id, attr.ppid.id,
442*f8e20a9fSScott Feldman 				   attr.ppid.id_len))
443b5d6fbdeSScott Feldman 				return NULL;
444b5d6fbdeSScott Feldman 		}
445b5d6fbdeSScott Feldman 
446*f8e20a9fSScott Feldman 		prev_attr = attr;
447b5d6fbdeSScott Feldman 	}
448b5d6fbdeSScott Feldman 
449b5d6fbdeSScott Feldman 	return dev;
450b5d6fbdeSScott Feldman }
451b5d6fbdeSScott Feldman 
4525e8d9049SScott Feldman /**
453ebb9a03aSJiri Pirko  *	switchdev_fib_ipv4_add - Add IPv4 route entry to switch
4545e8d9049SScott Feldman  *
4555e8d9049SScott Feldman  *	@dst: route's IPv4 destination address
4565e8d9049SScott Feldman  *	@dst_len: destination address length (prefix length)
4575e8d9049SScott Feldman  *	@fi: route FIB info structure
4585e8d9049SScott Feldman  *	@tos: route TOS
4595e8d9049SScott Feldman  *	@type: route type
460f8f21471SScott Feldman  *	@nlflags: netlink flags passed in (NLM_F_*)
4615e8d9049SScott Feldman  *	@tb_id: route table ID
4625e8d9049SScott Feldman  *
4635e8d9049SScott Feldman  *	Add IPv4 route entry to switch device.
4645e8d9049SScott Feldman  */
465ebb9a03aSJiri Pirko int switchdev_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
466f8f21471SScott Feldman 			   u8 tos, u8 type, u32 nlflags, u32 tb_id)
4675e8d9049SScott Feldman {
468b5d6fbdeSScott Feldman 	struct net_device *dev;
4699d47c0a2SJiri Pirko 	const struct switchdev_ops *ops;
470b5d6fbdeSScott Feldman 	int err = 0;
471b5d6fbdeSScott Feldman 
4728e05fd71SScott Feldman 	/* Don't offload route if using custom ip rules or if
4738e05fd71SScott Feldman 	 * IPv4 FIB offloading has been disabled completely.
4748e05fd71SScott Feldman 	 */
4758e05fd71SScott Feldman 
476e1315db1SScott Feldman #ifdef CONFIG_IP_MULTIPLE_TABLES
477e1315db1SScott Feldman 	if (fi->fib_net->ipv4.fib_has_custom_rules)
478e1315db1SScott Feldman 		return 0;
479e1315db1SScott Feldman #endif
480e1315db1SScott Feldman 
481e1315db1SScott Feldman 	if (fi->fib_net->ipv4.fib_offload_disabled)
482104616e7SScott Feldman 		return 0;
483104616e7SScott Feldman 
484ebb9a03aSJiri Pirko 	dev = switchdev_get_dev_by_nhs(fi);
485b5d6fbdeSScott Feldman 	if (!dev)
4865e8d9049SScott Feldman 		return 0;
4879d47c0a2SJiri Pirko 	ops = dev->switchdev_ops;
488b5d6fbdeSScott Feldman 
4899d47c0a2SJiri Pirko 	if (ops->switchdev_fib_ipv4_add) {
4909d47c0a2SJiri Pirko 		err = ops->switchdev_fib_ipv4_add(dev, htonl(dst), dst_len,
491f8f21471SScott Feldman 						  fi, tos, type, nlflags,
492f8f21471SScott Feldman 						  tb_id);
493b5d6fbdeSScott Feldman 		if (!err)
494b5d6fbdeSScott Feldman 			fi->fib_flags |= RTNH_F_EXTERNAL;
495b5d6fbdeSScott Feldman 	}
496b5d6fbdeSScott Feldman 
497b5d6fbdeSScott Feldman 	return err;
4985e8d9049SScott Feldman }
499ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_add);
5005e8d9049SScott Feldman 
5015e8d9049SScott Feldman /**
502ebb9a03aSJiri Pirko  *	switchdev_fib_ipv4_del - Delete IPv4 route entry from switch
5035e8d9049SScott Feldman  *
5045e8d9049SScott Feldman  *	@dst: route's IPv4 destination address
5055e8d9049SScott Feldman  *	@dst_len: destination address length (prefix length)
5065e8d9049SScott Feldman  *	@fi: route FIB info structure
5075e8d9049SScott Feldman  *	@tos: route TOS
5085e8d9049SScott Feldman  *	@type: route type
5095e8d9049SScott Feldman  *	@tb_id: route table ID
5105e8d9049SScott Feldman  *
5115e8d9049SScott Feldman  *	Delete IPv4 route entry from switch device.
5125e8d9049SScott Feldman  */
513ebb9a03aSJiri Pirko int switchdev_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
5145e8d9049SScott Feldman 			   u8 tos, u8 type, u32 tb_id)
5155e8d9049SScott Feldman {
516b5d6fbdeSScott Feldman 	struct net_device *dev;
5179d47c0a2SJiri Pirko 	const struct switchdev_ops *ops;
518b5d6fbdeSScott Feldman 	int err = 0;
519b5d6fbdeSScott Feldman 
520b5d6fbdeSScott Feldman 	if (!(fi->fib_flags & RTNH_F_EXTERNAL))
5215e8d9049SScott Feldman 		return 0;
522b5d6fbdeSScott Feldman 
523ebb9a03aSJiri Pirko 	dev = switchdev_get_dev_by_nhs(fi);
524b5d6fbdeSScott Feldman 	if (!dev)
525b5d6fbdeSScott Feldman 		return 0;
5269d47c0a2SJiri Pirko 	ops = dev->switchdev_ops;
527b5d6fbdeSScott Feldman 
5289d47c0a2SJiri Pirko 	if (ops->switchdev_fib_ipv4_del) {
5299d47c0a2SJiri Pirko 		err = ops->switchdev_fib_ipv4_del(dev, htonl(dst), dst_len,
530b5d6fbdeSScott Feldman 						  fi, tos, type, tb_id);
531b5d6fbdeSScott Feldman 		if (!err)
532b5d6fbdeSScott Feldman 			fi->fib_flags &= ~RTNH_F_EXTERNAL;
533b5d6fbdeSScott Feldman 	}
534b5d6fbdeSScott Feldman 
535b5d6fbdeSScott Feldman 	return err;
5365e8d9049SScott Feldman }
537ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_del);
5388e05fd71SScott Feldman 
5398e05fd71SScott Feldman /**
540ebb9a03aSJiri Pirko  *	switchdev_fib_ipv4_abort - Abort an IPv4 FIB operation
5418e05fd71SScott Feldman  *
5428e05fd71SScott Feldman  *	@fi: route FIB info structure
5438e05fd71SScott Feldman  */
544ebb9a03aSJiri Pirko void switchdev_fib_ipv4_abort(struct fib_info *fi)
5458e05fd71SScott Feldman {
5468e05fd71SScott Feldman 	/* There was a problem installing this route to the offload
5478e05fd71SScott Feldman 	 * device.  For now, until we come up with more refined
5488e05fd71SScott Feldman 	 * policy handling, abruptly end IPv4 fib offloading for
5498e05fd71SScott Feldman 	 * for entire net by flushing offload device(s) of all
5508e05fd71SScott Feldman 	 * IPv4 routes, and mark IPv4 fib offloading broken from
5518e05fd71SScott Feldman 	 * this point forward.
5528e05fd71SScott Feldman 	 */
5538e05fd71SScott Feldman 
5548e05fd71SScott Feldman 	fib_flush_external(fi->fib_net);
5558e05fd71SScott Feldman 	fi->fib_net->ipv4.fib_offload_disabled = true;
5568e05fd71SScott Feldman }
557ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_abort);
558