xref: /linux-6.15/net/switchdev/switchdev.c (revision 491d0f15)
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 
190*491d0f15SScott Feldman int __switchdev_port_obj_add(struct net_device *dev, struct switchdev_obj *obj)
191*491d0f15SScott Feldman {
192*491d0f15SScott Feldman 	const struct switchdev_ops *ops = dev->switchdev_ops;
193*491d0f15SScott Feldman 	struct net_device *lower_dev;
194*491d0f15SScott Feldman 	struct list_head *iter;
195*491d0f15SScott Feldman 	int err = -EOPNOTSUPP;
196*491d0f15SScott Feldman 
197*491d0f15SScott Feldman 	if (ops && ops->switchdev_port_obj_add)
198*491d0f15SScott Feldman 		return ops->switchdev_port_obj_add(dev, obj);
199*491d0f15SScott Feldman 
200*491d0f15SScott Feldman 	/* Switch device port(s) may be stacked under
201*491d0f15SScott Feldman 	 * bond/team/vlan dev, so recurse down to add object on
202*491d0f15SScott Feldman 	 * each port.
203*491d0f15SScott Feldman 	 */
204*491d0f15SScott Feldman 
205*491d0f15SScott Feldman 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
206*491d0f15SScott Feldman 		err = __switchdev_port_obj_add(lower_dev, obj);
207*491d0f15SScott Feldman 		if (err)
208*491d0f15SScott Feldman 			break;
209*491d0f15SScott Feldman 	}
210*491d0f15SScott Feldman 
211*491d0f15SScott Feldman 	return err;
212*491d0f15SScott Feldman }
213*491d0f15SScott Feldman 
214*491d0f15SScott Feldman /**
215*491d0f15SScott Feldman  *	switchdev_port_obj_add - Add port object
216*491d0f15SScott Feldman  *
217*491d0f15SScott Feldman  *	@dev: port device
218*491d0f15SScott Feldman  *	@obj: object to add
219*491d0f15SScott Feldman  *
220*491d0f15SScott Feldman  *	Use a 2-phase prepare-commit transaction model to ensure
221*491d0f15SScott Feldman  *	system is not left in a partially updated state due to
222*491d0f15SScott Feldman  *	failure from driver/device.
223*491d0f15SScott Feldman  *
224*491d0f15SScott Feldman  *	rtnl_lock must be held.
225*491d0f15SScott Feldman  */
226*491d0f15SScott Feldman int switchdev_port_obj_add(struct net_device *dev, struct switchdev_obj *obj)
227*491d0f15SScott Feldman {
228*491d0f15SScott Feldman 	int err;
229*491d0f15SScott Feldman 
230*491d0f15SScott Feldman 	ASSERT_RTNL();
231*491d0f15SScott Feldman 
232*491d0f15SScott Feldman 	/* Phase I: prepare for obj add. Driver/device should fail
233*491d0f15SScott Feldman 	 * here if there are going to be issues in the commit phase,
234*491d0f15SScott Feldman 	 * such as lack of resources or support.  The driver/device
235*491d0f15SScott Feldman 	 * should reserve resources needed for the commit phase here,
236*491d0f15SScott Feldman 	 * but should not commit the obj.
237*491d0f15SScott Feldman 	 */
238*491d0f15SScott Feldman 
239*491d0f15SScott Feldman 	obj->trans = SWITCHDEV_TRANS_PREPARE;
240*491d0f15SScott Feldman 	err = __switchdev_port_obj_add(dev, obj);
241*491d0f15SScott Feldman 	if (err) {
242*491d0f15SScott Feldman 		/* Prepare phase failed: abort the transaction.  Any
243*491d0f15SScott Feldman 		 * resources reserved in the prepare phase are
244*491d0f15SScott Feldman 		 * released.
245*491d0f15SScott Feldman 		 */
246*491d0f15SScott Feldman 
247*491d0f15SScott Feldman 		obj->trans = SWITCHDEV_TRANS_ABORT;
248*491d0f15SScott Feldman 		__switchdev_port_obj_add(dev, obj);
249*491d0f15SScott Feldman 
250*491d0f15SScott Feldman 		return err;
251*491d0f15SScott Feldman 	}
252*491d0f15SScott Feldman 
253*491d0f15SScott Feldman 	/* Phase II: commit obj add.  This cannot fail as a fault
254*491d0f15SScott Feldman 	 * of driver/device.  If it does, it's a bug in the driver/device
255*491d0f15SScott Feldman 	 * because the driver said everythings was OK in phase I.
256*491d0f15SScott Feldman 	 */
257*491d0f15SScott Feldman 
258*491d0f15SScott Feldman 	obj->trans = SWITCHDEV_TRANS_COMMIT;
259*491d0f15SScott Feldman 	err = __switchdev_port_obj_add(dev, obj);
260*491d0f15SScott Feldman 	WARN(err, "%s: Commit of object (id=%d) failed.\n", dev->name, obj->id);
261*491d0f15SScott Feldman 
262*491d0f15SScott Feldman 	return err;
263*491d0f15SScott Feldman }
264*491d0f15SScott Feldman EXPORT_SYMBOL_GPL(switchdev_port_obj_add);
265*491d0f15SScott Feldman 
266*491d0f15SScott Feldman /**
267*491d0f15SScott Feldman  *	switchdev_port_obj_del - Delete port object
268*491d0f15SScott Feldman  *
269*491d0f15SScott Feldman  *	@dev: port device
270*491d0f15SScott Feldman  *	@obj: object to delete
271*491d0f15SScott Feldman  */
272*491d0f15SScott Feldman int switchdev_port_obj_del(struct net_device *dev, struct switchdev_obj *obj)
273*491d0f15SScott Feldman {
274*491d0f15SScott Feldman 	const struct switchdev_ops *ops = dev->switchdev_ops;
275*491d0f15SScott Feldman 	struct net_device *lower_dev;
276*491d0f15SScott Feldman 	struct list_head *iter;
277*491d0f15SScott Feldman 	int err = -EOPNOTSUPP;
278*491d0f15SScott Feldman 
279*491d0f15SScott Feldman 	if (ops && ops->switchdev_port_obj_del)
280*491d0f15SScott Feldman 		return ops->switchdev_port_obj_del(dev, obj);
281*491d0f15SScott Feldman 
282*491d0f15SScott Feldman 	/* Switch device port(s) may be stacked under
283*491d0f15SScott Feldman 	 * bond/team/vlan dev, so recurse down to delete object on
284*491d0f15SScott Feldman 	 * each port.
285*491d0f15SScott Feldman 	 */
286*491d0f15SScott Feldman 
287*491d0f15SScott Feldman 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
288*491d0f15SScott Feldman 		err = switchdev_port_obj_del(lower_dev, obj);
289*491d0f15SScott Feldman 		if (err)
290*491d0f15SScott Feldman 			break;
291*491d0f15SScott Feldman 	}
292*491d0f15SScott Feldman 
293*491d0f15SScott Feldman 	return err;
294*491d0f15SScott Feldman }
295*491d0f15SScott Feldman EXPORT_SYMBOL_GPL(switchdev_port_obj_del);
296*491d0f15SScott Feldman 
297ebb9a03aSJiri Pirko static DEFINE_MUTEX(switchdev_mutex);
298ebb9a03aSJiri Pirko static RAW_NOTIFIER_HEAD(switchdev_notif_chain);
29903bf0c28SJiri Pirko 
30003bf0c28SJiri Pirko /**
301ebb9a03aSJiri Pirko  *	register_switchdev_notifier - Register notifier
30203bf0c28SJiri Pirko  *	@nb: notifier_block
30303bf0c28SJiri Pirko  *
30403bf0c28SJiri Pirko  *	Register switch device notifier. This should be used by code
30503bf0c28SJiri Pirko  *	which needs to monitor events happening in particular device.
30603bf0c28SJiri Pirko  *	Return values are same as for atomic_notifier_chain_register().
30703bf0c28SJiri Pirko  */
308ebb9a03aSJiri Pirko int register_switchdev_notifier(struct notifier_block *nb)
30903bf0c28SJiri Pirko {
31003bf0c28SJiri Pirko 	int err;
31103bf0c28SJiri Pirko 
312ebb9a03aSJiri Pirko 	mutex_lock(&switchdev_mutex);
313ebb9a03aSJiri Pirko 	err = raw_notifier_chain_register(&switchdev_notif_chain, nb);
314ebb9a03aSJiri Pirko 	mutex_unlock(&switchdev_mutex);
31503bf0c28SJiri Pirko 	return err;
31603bf0c28SJiri Pirko }
317ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(register_switchdev_notifier);
31803bf0c28SJiri Pirko 
31903bf0c28SJiri Pirko /**
320ebb9a03aSJiri Pirko  *	unregister_switchdev_notifier - Unregister notifier
32103bf0c28SJiri Pirko  *	@nb: notifier_block
32203bf0c28SJiri Pirko  *
32303bf0c28SJiri Pirko  *	Unregister switch device notifier.
32403bf0c28SJiri Pirko  *	Return values are same as for atomic_notifier_chain_unregister().
32503bf0c28SJiri Pirko  */
326ebb9a03aSJiri Pirko int unregister_switchdev_notifier(struct notifier_block *nb)
32703bf0c28SJiri Pirko {
32803bf0c28SJiri Pirko 	int err;
32903bf0c28SJiri Pirko 
330ebb9a03aSJiri Pirko 	mutex_lock(&switchdev_mutex);
331ebb9a03aSJiri Pirko 	err = raw_notifier_chain_unregister(&switchdev_notif_chain, nb);
332ebb9a03aSJiri Pirko 	mutex_unlock(&switchdev_mutex);
33303bf0c28SJiri Pirko 	return err;
33403bf0c28SJiri Pirko }
335ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(unregister_switchdev_notifier);
33603bf0c28SJiri Pirko 
33703bf0c28SJiri Pirko /**
338ebb9a03aSJiri Pirko  *	call_switchdev_notifiers - Call notifiers
33903bf0c28SJiri Pirko  *	@val: value passed unmodified to notifier function
34003bf0c28SJiri Pirko  *	@dev: port device
34103bf0c28SJiri Pirko  *	@info: notifier information data
34203bf0c28SJiri Pirko  *
34303bf0c28SJiri Pirko  *	Call all network notifier blocks. This should be called by driver
34403bf0c28SJiri Pirko  *	when it needs to propagate hardware event.
34503bf0c28SJiri Pirko  *	Return values are same as for atomic_notifier_call_chain().
34603bf0c28SJiri Pirko  */
347ebb9a03aSJiri Pirko int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
348ebb9a03aSJiri Pirko 			     struct switchdev_notifier_info *info)
34903bf0c28SJiri Pirko {
35003bf0c28SJiri Pirko 	int err;
35103bf0c28SJiri Pirko 
35203bf0c28SJiri Pirko 	info->dev = dev;
353ebb9a03aSJiri Pirko 	mutex_lock(&switchdev_mutex);
354ebb9a03aSJiri Pirko 	err = raw_notifier_call_chain(&switchdev_notif_chain, val, info);
355ebb9a03aSJiri Pirko 	mutex_unlock(&switchdev_mutex);
35603bf0c28SJiri Pirko 	return err;
35703bf0c28SJiri Pirko }
358ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
3598a44dbb2SRoopa Prabhu 
3608a44dbb2SRoopa Prabhu /**
361ebb9a03aSJiri Pirko  *	switchdev_port_bridge_setlink - Notify switch device port of bridge
3628a44dbb2SRoopa Prabhu  *	port attributes
3638a44dbb2SRoopa Prabhu  *
3648a44dbb2SRoopa Prabhu  *	@dev: port device
3658a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
3668a44dbb2SRoopa Prabhu  *	@flags: bridge setlink flags
3678a44dbb2SRoopa Prabhu  *
3688a44dbb2SRoopa Prabhu  *	Notify switch device port of bridge port attributes
3698a44dbb2SRoopa Prabhu  */
370ebb9a03aSJiri Pirko int switchdev_port_bridge_setlink(struct net_device *dev,
3718a44dbb2SRoopa Prabhu 				  struct nlmsghdr *nlh, u16 flags)
3728a44dbb2SRoopa Prabhu {
3738a44dbb2SRoopa Prabhu 	const struct net_device_ops *ops = dev->netdev_ops;
3748a44dbb2SRoopa Prabhu 
3758a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
3768a44dbb2SRoopa Prabhu 		return 0;
3778a44dbb2SRoopa Prabhu 
3788a44dbb2SRoopa Prabhu 	if (!ops->ndo_bridge_setlink)
3798a44dbb2SRoopa Prabhu 		return -EOPNOTSUPP;
3808a44dbb2SRoopa Prabhu 
3818a44dbb2SRoopa Prabhu 	return ops->ndo_bridge_setlink(dev, nlh, flags);
3828a44dbb2SRoopa Prabhu }
383ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_port_bridge_setlink);
3848a44dbb2SRoopa Prabhu 
3858a44dbb2SRoopa Prabhu /**
386ebb9a03aSJiri Pirko  *	switchdev_port_bridge_dellink - Notify switch device port of bridge
3878a44dbb2SRoopa Prabhu  *	port attribute delete
3888a44dbb2SRoopa Prabhu  *
3898a44dbb2SRoopa Prabhu  *	@dev: port device
3908a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
3918a44dbb2SRoopa Prabhu  *	@flags: bridge setlink flags
3928a44dbb2SRoopa Prabhu  *
3938a44dbb2SRoopa Prabhu  *	Notify switch device port of bridge port attribute delete
3948a44dbb2SRoopa Prabhu  */
395ebb9a03aSJiri Pirko int switchdev_port_bridge_dellink(struct net_device *dev,
3968a44dbb2SRoopa Prabhu 				  struct nlmsghdr *nlh, u16 flags)
3978a44dbb2SRoopa Prabhu {
3988a44dbb2SRoopa Prabhu 	const struct net_device_ops *ops = dev->netdev_ops;
3998a44dbb2SRoopa Prabhu 
4008a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
4018a44dbb2SRoopa Prabhu 		return 0;
4028a44dbb2SRoopa Prabhu 
4038a44dbb2SRoopa Prabhu 	if (!ops->ndo_bridge_dellink)
4048a44dbb2SRoopa Prabhu 		return -EOPNOTSUPP;
4058a44dbb2SRoopa Prabhu 
4068a44dbb2SRoopa Prabhu 	return ops->ndo_bridge_dellink(dev, nlh, flags);
4078a44dbb2SRoopa Prabhu }
408ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_port_bridge_dellink);
4098a44dbb2SRoopa Prabhu 
4108a44dbb2SRoopa Prabhu /**
411ebb9a03aSJiri Pirko  *	ndo_dflt_switchdev_port_bridge_setlink - default ndo bridge setlink
4128a44dbb2SRoopa Prabhu  *						 op for master devices
4138a44dbb2SRoopa Prabhu  *
4148a44dbb2SRoopa Prabhu  *	@dev: port device
4158a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
4168a44dbb2SRoopa Prabhu  *	@flags: bridge setlink flags
4178a44dbb2SRoopa Prabhu  *
4188a44dbb2SRoopa Prabhu  *	Notify master device slaves of bridge port attributes
4198a44dbb2SRoopa Prabhu  */
420ebb9a03aSJiri Pirko int ndo_dflt_switchdev_port_bridge_setlink(struct net_device *dev,
4218a44dbb2SRoopa Prabhu 					   struct nlmsghdr *nlh, u16 flags)
4228a44dbb2SRoopa Prabhu {
4238a44dbb2SRoopa Prabhu 	struct net_device *lower_dev;
4248a44dbb2SRoopa Prabhu 	struct list_head *iter;
4258a44dbb2SRoopa Prabhu 	int ret = 0, err = 0;
4268a44dbb2SRoopa Prabhu 
4278a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
4288a44dbb2SRoopa Prabhu 		return ret;
4298a44dbb2SRoopa Prabhu 
4308a44dbb2SRoopa Prabhu 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
431ebb9a03aSJiri Pirko 		err = switchdev_port_bridge_setlink(lower_dev, nlh, flags);
4328a44dbb2SRoopa Prabhu 		if (err && err != -EOPNOTSUPP)
4338a44dbb2SRoopa Prabhu 			ret = err;
4348a44dbb2SRoopa Prabhu 	}
4358a44dbb2SRoopa Prabhu 
4368a44dbb2SRoopa Prabhu 	return ret;
4378a44dbb2SRoopa Prabhu }
438ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(ndo_dflt_switchdev_port_bridge_setlink);
4398a44dbb2SRoopa Prabhu 
4408a44dbb2SRoopa Prabhu /**
441ebb9a03aSJiri Pirko  *	ndo_dflt_switchdev_port_bridge_dellink - default ndo bridge dellink
4428a44dbb2SRoopa Prabhu  *						 op for master devices
4438a44dbb2SRoopa Prabhu  *
4448a44dbb2SRoopa Prabhu  *	@dev: port device
4458a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
4468a44dbb2SRoopa Prabhu  *	@flags: bridge dellink flags
4478a44dbb2SRoopa Prabhu  *
4488a44dbb2SRoopa Prabhu  *	Notify master device slaves of bridge port attribute deletes
4498a44dbb2SRoopa Prabhu  */
450ebb9a03aSJiri Pirko int ndo_dflt_switchdev_port_bridge_dellink(struct net_device *dev,
4518a44dbb2SRoopa Prabhu 					   struct nlmsghdr *nlh, u16 flags)
4528a44dbb2SRoopa Prabhu {
4538a44dbb2SRoopa Prabhu 	struct net_device *lower_dev;
4548a44dbb2SRoopa Prabhu 	struct list_head *iter;
4558a44dbb2SRoopa Prabhu 	int ret = 0, err = 0;
4568a44dbb2SRoopa Prabhu 
4578a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
4588a44dbb2SRoopa Prabhu 		return ret;
4598a44dbb2SRoopa Prabhu 
4608a44dbb2SRoopa Prabhu 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
461ebb9a03aSJiri Pirko 		err = switchdev_port_bridge_dellink(lower_dev, nlh, flags);
4628a44dbb2SRoopa Prabhu 		if (err && err != -EOPNOTSUPP)
4638a44dbb2SRoopa Prabhu 			ret = err;
4648a44dbb2SRoopa Prabhu 	}
4658a44dbb2SRoopa Prabhu 
4668a44dbb2SRoopa Prabhu 	return ret;
4678a44dbb2SRoopa Prabhu }
468ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(ndo_dflt_switchdev_port_bridge_dellink);
4695e8d9049SScott Feldman 
470ebb9a03aSJiri Pirko static struct net_device *switchdev_get_lowest_dev(struct net_device *dev)
471b5d6fbdeSScott Feldman {
4729d47c0a2SJiri Pirko 	const struct switchdev_ops *ops = dev->switchdev_ops;
473b5d6fbdeSScott Feldman 	struct net_device *lower_dev;
474b5d6fbdeSScott Feldman 	struct net_device *port_dev;
475b5d6fbdeSScott Feldman 	struct list_head *iter;
476b5d6fbdeSScott Feldman 
477b5d6fbdeSScott Feldman 	/* Recusively search down until we find a sw port dev.
478f8e20a9fSScott Feldman 	 * (A sw port dev supports switchdev_port_attr_get).
479b5d6fbdeSScott Feldman 	 */
480b5d6fbdeSScott Feldman 
481f8e20a9fSScott Feldman 	if (ops && ops->switchdev_port_attr_get)
482b5d6fbdeSScott Feldman 		return dev;
483b5d6fbdeSScott Feldman 
484b5d6fbdeSScott Feldman 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
485ebb9a03aSJiri Pirko 		port_dev = switchdev_get_lowest_dev(lower_dev);
486b5d6fbdeSScott Feldman 		if (port_dev)
487b5d6fbdeSScott Feldman 			return port_dev;
488b5d6fbdeSScott Feldman 	}
489b5d6fbdeSScott Feldman 
490b5d6fbdeSScott Feldman 	return NULL;
491b5d6fbdeSScott Feldman }
492b5d6fbdeSScott Feldman 
493ebb9a03aSJiri Pirko static struct net_device *switchdev_get_dev_by_nhs(struct fib_info *fi)
494b5d6fbdeSScott Feldman {
495f8e20a9fSScott Feldman 	struct switchdev_attr attr = {
496f8e20a9fSScott Feldman 		.id = SWITCHDEV_ATTR_PORT_PARENT_ID,
497f8e20a9fSScott Feldman 	};
498f8e20a9fSScott Feldman 	struct switchdev_attr prev_attr;
499b5d6fbdeSScott Feldman 	struct net_device *dev = NULL;
500b5d6fbdeSScott Feldman 	int nhsel;
501b5d6fbdeSScott Feldman 
502b5d6fbdeSScott Feldman 	/* For this route, all nexthop devs must be on the same switch. */
503b5d6fbdeSScott Feldman 
504b5d6fbdeSScott Feldman 	for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) {
505b5d6fbdeSScott Feldman 		const struct fib_nh *nh = &fi->fib_nh[nhsel];
506b5d6fbdeSScott Feldman 
507b5d6fbdeSScott Feldman 		if (!nh->nh_dev)
508b5d6fbdeSScott Feldman 			return NULL;
509b5d6fbdeSScott Feldman 
510ebb9a03aSJiri Pirko 		dev = switchdev_get_lowest_dev(nh->nh_dev);
511b5d6fbdeSScott Feldman 		if (!dev)
512b5d6fbdeSScott Feldman 			return NULL;
513b5d6fbdeSScott Feldman 
514f8e20a9fSScott Feldman 		if (switchdev_port_attr_get(dev, &attr))
515b5d6fbdeSScott Feldman 			return NULL;
516b5d6fbdeSScott Feldman 
517b5d6fbdeSScott Feldman 		if (nhsel > 0) {
518f8e20a9fSScott Feldman 			if (prev_attr.ppid.id_len != attr.ppid.id_len)
519b5d6fbdeSScott Feldman 				return NULL;
520f8e20a9fSScott Feldman 			if (memcmp(prev_attr.ppid.id, attr.ppid.id,
521f8e20a9fSScott Feldman 				   attr.ppid.id_len))
522b5d6fbdeSScott Feldman 				return NULL;
523b5d6fbdeSScott Feldman 		}
524b5d6fbdeSScott Feldman 
525f8e20a9fSScott Feldman 		prev_attr = attr;
526b5d6fbdeSScott Feldman 	}
527b5d6fbdeSScott Feldman 
528b5d6fbdeSScott Feldman 	return dev;
529b5d6fbdeSScott Feldman }
530b5d6fbdeSScott Feldman 
5315e8d9049SScott Feldman /**
532ebb9a03aSJiri Pirko  *	switchdev_fib_ipv4_add - Add IPv4 route entry to switch
5335e8d9049SScott Feldman  *
5345e8d9049SScott Feldman  *	@dst: route's IPv4 destination address
5355e8d9049SScott Feldman  *	@dst_len: destination address length (prefix length)
5365e8d9049SScott Feldman  *	@fi: route FIB info structure
5375e8d9049SScott Feldman  *	@tos: route TOS
5385e8d9049SScott Feldman  *	@type: route type
539f8f21471SScott Feldman  *	@nlflags: netlink flags passed in (NLM_F_*)
5405e8d9049SScott Feldman  *	@tb_id: route table ID
5415e8d9049SScott Feldman  *
5425e8d9049SScott Feldman  *	Add IPv4 route entry to switch device.
5435e8d9049SScott Feldman  */
544ebb9a03aSJiri Pirko int switchdev_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
545f8f21471SScott Feldman 			   u8 tos, u8 type, u32 nlflags, u32 tb_id)
5465e8d9049SScott Feldman {
547b5d6fbdeSScott Feldman 	struct net_device *dev;
5489d47c0a2SJiri Pirko 	const struct switchdev_ops *ops;
549b5d6fbdeSScott Feldman 	int err = 0;
550b5d6fbdeSScott Feldman 
5518e05fd71SScott Feldman 	/* Don't offload route if using custom ip rules or if
5528e05fd71SScott Feldman 	 * IPv4 FIB offloading has been disabled completely.
5538e05fd71SScott Feldman 	 */
5548e05fd71SScott Feldman 
555e1315db1SScott Feldman #ifdef CONFIG_IP_MULTIPLE_TABLES
556e1315db1SScott Feldman 	if (fi->fib_net->ipv4.fib_has_custom_rules)
557e1315db1SScott Feldman 		return 0;
558e1315db1SScott Feldman #endif
559e1315db1SScott Feldman 
560e1315db1SScott Feldman 	if (fi->fib_net->ipv4.fib_offload_disabled)
561104616e7SScott Feldman 		return 0;
562104616e7SScott Feldman 
563ebb9a03aSJiri Pirko 	dev = switchdev_get_dev_by_nhs(fi);
564b5d6fbdeSScott Feldman 	if (!dev)
5655e8d9049SScott Feldman 		return 0;
5669d47c0a2SJiri Pirko 	ops = dev->switchdev_ops;
567b5d6fbdeSScott Feldman 
5689d47c0a2SJiri Pirko 	if (ops->switchdev_fib_ipv4_add) {
5699d47c0a2SJiri Pirko 		err = ops->switchdev_fib_ipv4_add(dev, htonl(dst), dst_len,
570f8f21471SScott Feldman 						  fi, tos, type, nlflags,
571f8f21471SScott Feldman 						  tb_id);
572b5d6fbdeSScott Feldman 		if (!err)
573b5d6fbdeSScott Feldman 			fi->fib_flags |= RTNH_F_EXTERNAL;
574b5d6fbdeSScott Feldman 	}
575b5d6fbdeSScott Feldman 
576b5d6fbdeSScott Feldman 	return err;
5775e8d9049SScott Feldman }
578ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_add);
5795e8d9049SScott Feldman 
5805e8d9049SScott Feldman /**
581ebb9a03aSJiri Pirko  *	switchdev_fib_ipv4_del - Delete IPv4 route entry from switch
5825e8d9049SScott Feldman  *
5835e8d9049SScott Feldman  *	@dst: route's IPv4 destination address
5845e8d9049SScott Feldman  *	@dst_len: destination address length (prefix length)
5855e8d9049SScott Feldman  *	@fi: route FIB info structure
5865e8d9049SScott Feldman  *	@tos: route TOS
5875e8d9049SScott Feldman  *	@type: route type
5885e8d9049SScott Feldman  *	@tb_id: route table ID
5895e8d9049SScott Feldman  *
5905e8d9049SScott Feldman  *	Delete IPv4 route entry from switch device.
5915e8d9049SScott Feldman  */
592ebb9a03aSJiri Pirko int switchdev_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
5935e8d9049SScott Feldman 			   u8 tos, u8 type, u32 tb_id)
5945e8d9049SScott Feldman {
595b5d6fbdeSScott Feldman 	struct net_device *dev;
5969d47c0a2SJiri Pirko 	const struct switchdev_ops *ops;
597b5d6fbdeSScott Feldman 	int err = 0;
598b5d6fbdeSScott Feldman 
599b5d6fbdeSScott Feldman 	if (!(fi->fib_flags & RTNH_F_EXTERNAL))
6005e8d9049SScott Feldman 		return 0;
601b5d6fbdeSScott Feldman 
602ebb9a03aSJiri Pirko 	dev = switchdev_get_dev_by_nhs(fi);
603b5d6fbdeSScott Feldman 	if (!dev)
604b5d6fbdeSScott Feldman 		return 0;
6059d47c0a2SJiri Pirko 	ops = dev->switchdev_ops;
606b5d6fbdeSScott Feldman 
6079d47c0a2SJiri Pirko 	if (ops->switchdev_fib_ipv4_del) {
6089d47c0a2SJiri Pirko 		err = ops->switchdev_fib_ipv4_del(dev, htonl(dst), dst_len,
609b5d6fbdeSScott Feldman 						  fi, tos, type, tb_id);
610b5d6fbdeSScott Feldman 		if (!err)
611b5d6fbdeSScott Feldman 			fi->fib_flags &= ~RTNH_F_EXTERNAL;
612b5d6fbdeSScott Feldman 	}
613b5d6fbdeSScott Feldman 
614b5d6fbdeSScott Feldman 	return err;
6155e8d9049SScott Feldman }
616ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_del);
6178e05fd71SScott Feldman 
6188e05fd71SScott Feldman /**
619ebb9a03aSJiri Pirko  *	switchdev_fib_ipv4_abort - Abort an IPv4 FIB operation
6208e05fd71SScott Feldman  *
6218e05fd71SScott Feldman  *	@fi: route FIB info structure
6228e05fd71SScott Feldman  */
623ebb9a03aSJiri Pirko void switchdev_fib_ipv4_abort(struct fib_info *fi)
6248e05fd71SScott Feldman {
6258e05fd71SScott Feldman 	/* There was a problem installing this route to the offload
6268e05fd71SScott Feldman 	 * device.  For now, until we come up with more refined
6278e05fd71SScott Feldman 	 * policy handling, abruptly end IPv4 fib offloading for
6288e05fd71SScott Feldman 	 * for entire net by flushing offload device(s) of all
6298e05fd71SScott Feldman 	 * IPv4 routes, and mark IPv4 fib offloading broken from
6308e05fd71SScott Feldman 	 * this point forward.
6318e05fd71SScott Feldman 	 */
6328e05fd71SScott Feldman 
6338e05fd71SScott Feldman 	fib_flush_external(fi->fib_net);
6348e05fd71SScott Feldman 	fi->fib_net->ipv4.fib_offload_disabled = true;
6358e05fd71SScott Feldman }
636ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_abort);
637