xref: /linux-6.15/net/switchdev/switchdev.c (revision 2b0a5688)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2007f790cSJiri Pirko /*
3007f790cSJiri Pirko  * net/switchdev/switchdev.c - Switch device API
47ea6eb3fSJiri Pirko  * Copyright (c) 2014-2015 Jiri Pirko <[email protected]>
5f8f21471SScott Feldman  * Copyright (c) 2014-2015 Scott Feldman <[email protected]>
6007f790cSJiri Pirko  */
7007f790cSJiri Pirko 
8007f790cSJiri Pirko #include <linux/kernel.h>
9007f790cSJiri Pirko #include <linux/types.h>
10007f790cSJiri Pirko #include <linux/init.h>
1103bf0c28SJiri Pirko #include <linux/mutex.h>
1203bf0c28SJiri Pirko #include <linux/notifier.h>
13007f790cSJiri Pirko #include <linux/netdevice.h>
14850d0cbcSJiri Pirko #include <linux/etherdevice.h>
1547f8328bSScott Feldman #include <linux/if_bridge.h>
167ea6eb3fSJiri Pirko #include <linux/list.h>
17793f4014SJiri Pirko #include <linux/workqueue.h>
1887aaf2caSNikolay Aleksandrov #include <linux/if_vlan.h>
194f2c6ae5SIdo Schimmel #include <linux/rtnetlink.h>
20007f790cSJiri Pirko #include <net/switchdev.h>
21007f790cSJiri Pirko 
22793f4014SJiri Pirko static LIST_HEAD(deferred);
23793f4014SJiri Pirko static DEFINE_SPINLOCK(deferred_lock);
24793f4014SJiri Pirko 
25793f4014SJiri Pirko typedef void switchdev_deferred_func_t(struct net_device *dev,
26793f4014SJiri Pirko 				       const void *data);
27793f4014SJiri Pirko 
28793f4014SJiri Pirko struct switchdev_deferred_item {
29793f4014SJiri Pirko 	struct list_head list;
30793f4014SJiri Pirko 	struct net_device *dev;
31793f4014SJiri Pirko 	switchdev_deferred_func_t *func;
32fbfc8502SGustavo A. R. Silva 	unsigned long data[];
33793f4014SJiri Pirko };
34793f4014SJiri Pirko 
35793f4014SJiri Pirko static struct switchdev_deferred_item *switchdev_deferred_dequeue(void)
36793f4014SJiri Pirko {
37793f4014SJiri Pirko 	struct switchdev_deferred_item *dfitem;
38793f4014SJiri Pirko 
39793f4014SJiri Pirko 	spin_lock_bh(&deferred_lock);
40793f4014SJiri Pirko 	if (list_empty(&deferred)) {
41793f4014SJiri Pirko 		dfitem = NULL;
42793f4014SJiri Pirko 		goto unlock;
43793f4014SJiri Pirko 	}
44793f4014SJiri Pirko 	dfitem = list_first_entry(&deferred,
45793f4014SJiri Pirko 				  struct switchdev_deferred_item, list);
46793f4014SJiri Pirko 	list_del(&dfitem->list);
47793f4014SJiri Pirko unlock:
48793f4014SJiri Pirko 	spin_unlock_bh(&deferred_lock);
49793f4014SJiri Pirko 	return dfitem;
50793f4014SJiri Pirko }
51793f4014SJiri Pirko 
52793f4014SJiri Pirko /**
53793f4014SJiri Pirko  *	switchdev_deferred_process - Process ops in deferred queue
54793f4014SJiri Pirko  *
55793f4014SJiri Pirko  *	Called to flush the ops currently queued in deferred ops queue.
56793f4014SJiri Pirko  *	rtnl_lock must be held.
57793f4014SJiri Pirko  */
58793f4014SJiri Pirko void switchdev_deferred_process(void)
59793f4014SJiri Pirko {
60793f4014SJiri Pirko 	struct switchdev_deferred_item *dfitem;
61793f4014SJiri Pirko 
62793f4014SJiri Pirko 	ASSERT_RTNL();
63793f4014SJiri Pirko 
64793f4014SJiri Pirko 	while ((dfitem = switchdev_deferred_dequeue())) {
65793f4014SJiri Pirko 		dfitem->func(dfitem->dev, dfitem->data);
66793f4014SJiri Pirko 		dev_put(dfitem->dev);
67793f4014SJiri Pirko 		kfree(dfitem);
68793f4014SJiri Pirko 	}
69793f4014SJiri Pirko }
70793f4014SJiri Pirko EXPORT_SYMBOL_GPL(switchdev_deferred_process);
71793f4014SJiri Pirko 
72793f4014SJiri Pirko static void switchdev_deferred_process_work(struct work_struct *work)
73793f4014SJiri Pirko {
74793f4014SJiri Pirko 	rtnl_lock();
75793f4014SJiri Pirko 	switchdev_deferred_process();
76793f4014SJiri Pirko 	rtnl_unlock();
77793f4014SJiri Pirko }
78793f4014SJiri Pirko 
79793f4014SJiri Pirko static DECLARE_WORK(deferred_process_work, switchdev_deferred_process_work);
80793f4014SJiri Pirko 
81793f4014SJiri Pirko static int switchdev_deferred_enqueue(struct net_device *dev,
82793f4014SJiri Pirko 				      const void *data, size_t data_len,
83793f4014SJiri Pirko 				      switchdev_deferred_func_t *func)
84793f4014SJiri Pirko {
85793f4014SJiri Pirko 	struct switchdev_deferred_item *dfitem;
86793f4014SJiri Pirko 
87793f4014SJiri Pirko 	dfitem = kmalloc(sizeof(*dfitem) + data_len, GFP_ATOMIC);
88793f4014SJiri Pirko 	if (!dfitem)
89793f4014SJiri Pirko 		return -ENOMEM;
90793f4014SJiri Pirko 	dfitem->dev = dev;
91793f4014SJiri Pirko 	dfitem->func = func;
92793f4014SJiri Pirko 	memcpy(dfitem->data, data, data_len);
93793f4014SJiri Pirko 	dev_hold(dev);
94793f4014SJiri Pirko 	spin_lock_bh(&deferred_lock);
95793f4014SJiri Pirko 	list_add_tail(&dfitem->list, &deferred);
96793f4014SJiri Pirko 	spin_unlock_bh(&deferred_lock);
97793f4014SJiri Pirko 	schedule_work(&deferred_process_work);
98793f4014SJiri Pirko 	return 0;
99793f4014SJiri Pirko }
100793f4014SJiri Pirko 
101d45224d6SFlorian Fainelli static int switchdev_port_attr_notify(enum switchdev_notifier_type nt,
102d45224d6SFlorian Fainelli 				      struct net_device *dev,
103dcbdf135SVladimir Oltean 				      const struct switchdev_attr *attr,
104dcbdf135SVladimir Oltean 				      struct netlink_ext_ack *extack)
1053094333dSScott Feldman {
106d45224d6SFlorian Fainelli 	int err;
107d45224d6SFlorian Fainelli 	int rc;
1083094333dSScott Feldman 
109d45224d6SFlorian Fainelli 	struct switchdev_notifier_port_attr_info attr_info = {
110d45224d6SFlorian Fainelli 		.attr = attr,
111d45224d6SFlorian Fainelli 		.handled = false,
112d45224d6SFlorian Fainelli 	};
1133094333dSScott Feldman 
114d45224d6SFlorian Fainelli 	rc = call_switchdev_blocking_notifiers(nt, dev,
115dcbdf135SVladimir Oltean 					       &attr_info.info, extack);
116d45224d6SFlorian Fainelli 	err = notifier_to_errno(rc);
117d45224d6SFlorian Fainelli 	if (err) {
118d45224d6SFlorian Fainelli 		WARN_ON(!attr_info.handled);
1193094333dSScott Feldman 		return err;
1203094333dSScott Feldman 	}
1213094333dSScott Feldman 
122d45224d6SFlorian Fainelli 	if (!attr_info.handled)
123d45224d6SFlorian Fainelli 		return -EOPNOTSUPP;
124d45224d6SFlorian Fainelli 
125d45224d6SFlorian Fainelli 	return 0;
126d45224d6SFlorian Fainelli }
127d45224d6SFlorian Fainelli 
1280bc05d58SJiri Pirko static int switchdev_port_attr_set_now(struct net_device *dev,
129dcbdf135SVladimir Oltean 				       const struct switchdev_attr *attr,
130dcbdf135SVladimir Oltean 				       struct netlink_ext_ack *extack)
1313094333dSScott Feldman {
132dcbdf135SVladimir Oltean 	return switchdev_port_attr_notify(SWITCHDEV_PORT_ATTR_SET, dev, attr,
133dcbdf135SVladimir Oltean 					  extack);
1343094333dSScott Feldman }
1350bc05d58SJiri Pirko 
1360bc05d58SJiri Pirko static void switchdev_port_attr_set_deferred(struct net_device *dev,
1370bc05d58SJiri Pirko 					     const void *data)
1380bc05d58SJiri Pirko {
1390bc05d58SJiri Pirko 	const struct switchdev_attr *attr = data;
1400bc05d58SJiri Pirko 	int err;
1410bc05d58SJiri Pirko 
142dcbdf135SVladimir Oltean 	err = switchdev_port_attr_set_now(dev, attr, NULL);
1430bc05d58SJiri Pirko 	if (err && err != -EOPNOTSUPP)
1440bc05d58SJiri Pirko 		netdev_err(dev, "failed (err=%d) to set attribute (id=%d)\n",
1450bc05d58SJiri Pirko 			   err, attr->id);
1467ceb2afbSElad Raz 	if (attr->complete)
1477ceb2afbSElad Raz 		attr->complete(dev, err, attr->complete_priv);
1480bc05d58SJiri Pirko }
1490bc05d58SJiri Pirko 
1500bc05d58SJiri Pirko static int switchdev_port_attr_set_defer(struct net_device *dev,
1510bc05d58SJiri Pirko 					 const struct switchdev_attr *attr)
1520bc05d58SJiri Pirko {
1530bc05d58SJiri Pirko 	return switchdev_deferred_enqueue(dev, attr, sizeof(*attr),
1540bc05d58SJiri Pirko 					  switchdev_port_attr_set_deferred);
1550bc05d58SJiri Pirko }
1560bc05d58SJiri Pirko 
1570bc05d58SJiri Pirko /**
1580bc05d58SJiri Pirko  *	switchdev_port_attr_set - Set port attribute
1590bc05d58SJiri Pirko  *
1600bc05d58SJiri Pirko  *	@dev: port device
1610bc05d58SJiri Pirko  *	@attr: attribute to set
162dcbdf135SVladimir Oltean  *	@extack: netlink extended ack, for error message propagation
1630bc05d58SJiri Pirko  *
1640bc05d58SJiri Pirko  *	rtnl_lock must be held and must not be in atomic section,
1650bc05d58SJiri Pirko  *	in case SWITCHDEV_F_DEFER flag is not set.
1660bc05d58SJiri Pirko  */
1670bc05d58SJiri Pirko int switchdev_port_attr_set(struct net_device *dev,
168dcbdf135SVladimir Oltean 			    const struct switchdev_attr *attr,
169dcbdf135SVladimir Oltean 			    struct netlink_ext_ack *extack)
1700bc05d58SJiri Pirko {
1710bc05d58SJiri Pirko 	if (attr->flags & SWITCHDEV_F_DEFER)
1720bc05d58SJiri Pirko 		return switchdev_port_attr_set_defer(dev, attr);
1730bc05d58SJiri Pirko 	ASSERT_RTNL();
174dcbdf135SVladimir Oltean 	return switchdev_port_attr_set_now(dev, attr, extack);
1750bc05d58SJiri Pirko }
1763094333dSScott Feldman EXPORT_SYMBOL_GPL(switchdev_port_attr_set);
1773094333dSScott Feldman 
178e258d919SScott Feldman static size_t switchdev_obj_size(const struct switchdev_obj *obj)
179e258d919SScott Feldman {
180e258d919SScott Feldman 	switch (obj->id) {
181e258d919SScott Feldman 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
182e258d919SScott Feldman 		return sizeof(struct switchdev_obj_port_vlan);
1834d41e125SElad Raz 	case SWITCHDEV_OBJ_ID_PORT_MDB:
1844d41e125SElad Raz 		return sizeof(struct switchdev_obj_port_mdb);
18547d5b6dbSAndrew Lunn 	case SWITCHDEV_OBJ_ID_HOST_MDB:
18647d5b6dbSAndrew Lunn 		return sizeof(struct switchdev_obj_port_mdb);
187e258d919SScott Feldman 	default:
188e258d919SScott Feldman 		BUG();
189e258d919SScott Feldman 	}
190e258d919SScott Feldman 	return 0;
191e258d919SScott Feldman }
192e258d919SScott Feldman 
193d17d9f5eSPetr Machata static int switchdev_port_obj_notify(enum switchdev_notifier_type nt,
194d17d9f5eSPetr Machata 				     struct net_device *dev,
195648b4a99SJiri Pirko 				     const struct switchdev_obj *obj,
19669b7320eSPetr Machata 				     struct netlink_ext_ack *extack)
197491d0f15SScott Feldman {
198d17d9f5eSPetr Machata 	int rc;
199d17d9f5eSPetr Machata 	int err;
200491d0f15SScott Feldman 
201d17d9f5eSPetr Machata 	struct switchdev_notifier_port_obj_info obj_info = {
202d17d9f5eSPetr Machata 		.obj = obj,
203d17d9f5eSPetr Machata 		.handled = false,
204d17d9f5eSPetr Machata 	};
205491d0f15SScott Feldman 
206479c86dcSPetr Machata 	rc = call_switchdev_blocking_notifiers(nt, dev, &obj_info.info, extack);
207d17d9f5eSPetr Machata 	err = notifier_to_errno(rc);
208d17d9f5eSPetr Machata 	if (err) {
209d17d9f5eSPetr Machata 		WARN_ON(!obj_info.handled);
210491d0f15SScott Feldman 		return err;
211491d0f15SScott Feldman 	}
212d17d9f5eSPetr Machata 	if (!obj_info.handled)
213d17d9f5eSPetr Machata 		return -EOPNOTSUPP;
214d17d9f5eSPetr Machata 	return 0;
215d17d9f5eSPetr Machata }
216491d0f15SScott Feldman 
2174d429c5dSJiri Pirko static void switchdev_port_obj_add_deferred(struct net_device *dev,
2184d429c5dSJiri Pirko 					    const void *data)
2194d429c5dSJiri Pirko {
2204d429c5dSJiri Pirko 	const struct switchdev_obj *obj = data;
2214d429c5dSJiri Pirko 	int err;
2224d429c5dSJiri Pirko 
223cf6def51SVladimir Oltean 	ASSERT_RTNL();
224cf6def51SVladimir Oltean 	err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD,
225cf6def51SVladimir Oltean 					dev, obj, NULL);
2264d429c5dSJiri Pirko 	if (err && err != -EOPNOTSUPP)
2274d429c5dSJiri Pirko 		netdev_err(dev, "failed (err=%d) to add object (id=%d)\n",
2284d429c5dSJiri Pirko 			   err, obj->id);
2297ceb2afbSElad Raz 	if (obj->complete)
2307ceb2afbSElad Raz 		obj->complete(dev, err, obj->complete_priv);
2314d429c5dSJiri Pirko }
2324d429c5dSJiri Pirko 
2334d429c5dSJiri Pirko static int switchdev_port_obj_add_defer(struct net_device *dev,
2344d429c5dSJiri Pirko 					const struct switchdev_obj *obj)
2354d429c5dSJiri Pirko {
236e258d919SScott Feldman 	return switchdev_deferred_enqueue(dev, obj, switchdev_obj_size(obj),
2374d429c5dSJiri Pirko 					  switchdev_port_obj_add_deferred);
2384d429c5dSJiri Pirko }
239491d0f15SScott Feldman 
240491d0f15SScott Feldman /**
2414d429c5dSJiri Pirko  *	switchdev_port_obj_add - Add port object
242491d0f15SScott Feldman  *
243491d0f15SScott Feldman  *	@dev: port device
2444d429c5dSJiri Pirko  *	@obj: object to add
245c8af73f0SAndrew Lunn  *	@extack: netlink extended ack
2464d429c5dSJiri Pirko  *
2474d429c5dSJiri Pirko  *	rtnl_lock must be held and must not be in atomic section,
2484d429c5dSJiri Pirko  *	in case SWITCHDEV_F_DEFER flag is not set.
249491d0f15SScott Feldman  */
2504d429c5dSJiri Pirko int switchdev_port_obj_add(struct net_device *dev,
25169b7320eSPetr Machata 			   const struct switchdev_obj *obj,
25269b7320eSPetr Machata 			   struct netlink_ext_ack *extack)
2534d429c5dSJiri Pirko {
2544d429c5dSJiri Pirko 	if (obj->flags & SWITCHDEV_F_DEFER)
2554d429c5dSJiri Pirko 		return switchdev_port_obj_add_defer(dev, obj);
2564d429c5dSJiri Pirko 	ASSERT_RTNL();
257cf6def51SVladimir Oltean 	return switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD,
258cf6def51SVladimir Oltean 					 dev, obj, extack);
2594d429c5dSJiri Pirko }
2604d429c5dSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_port_obj_add);
2614d429c5dSJiri Pirko 
2624d429c5dSJiri Pirko static int switchdev_port_obj_del_now(struct net_device *dev,
263648b4a99SJiri Pirko 				      const struct switchdev_obj *obj)
264491d0f15SScott Feldman {
265d17d9f5eSPetr Machata 	return switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_DEL,
266ffb68fc5SVladimir Oltean 					 dev, obj, NULL);
267491d0f15SScott Feldman }
2684d429c5dSJiri Pirko 
2694d429c5dSJiri Pirko static void switchdev_port_obj_del_deferred(struct net_device *dev,
2704d429c5dSJiri Pirko 					    const void *data)
2714d429c5dSJiri Pirko {
2724d429c5dSJiri Pirko 	const struct switchdev_obj *obj = data;
2734d429c5dSJiri Pirko 	int err;
2744d429c5dSJiri Pirko 
2754d429c5dSJiri Pirko 	err = switchdev_port_obj_del_now(dev, obj);
2764d429c5dSJiri Pirko 	if (err && err != -EOPNOTSUPP)
2774d429c5dSJiri Pirko 		netdev_err(dev, "failed (err=%d) to del object (id=%d)\n",
2784d429c5dSJiri Pirko 			   err, obj->id);
2797ceb2afbSElad Raz 	if (obj->complete)
2807ceb2afbSElad Raz 		obj->complete(dev, err, obj->complete_priv);
2814d429c5dSJiri Pirko }
2824d429c5dSJiri Pirko 
2834d429c5dSJiri Pirko static int switchdev_port_obj_del_defer(struct net_device *dev,
2844d429c5dSJiri Pirko 					const struct switchdev_obj *obj)
2854d429c5dSJiri Pirko {
286e258d919SScott Feldman 	return switchdev_deferred_enqueue(dev, obj, switchdev_obj_size(obj),
2874d429c5dSJiri Pirko 					  switchdev_port_obj_del_deferred);
2884d429c5dSJiri Pirko }
2894d429c5dSJiri Pirko 
2904d429c5dSJiri Pirko /**
2914d429c5dSJiri Pirko  *	switchdev_port_obj_del - Delete port object
2924d429c5dSJiri Pirko  *
2934d429c5dSJiri Pirko  *	@dev: port device
2944d429c5dSJiri Pirko  *	@obj: object to delete
2954d429c5dSJiri Pirko  *
2964d429c5dSJiri Pirko  *	rtnl_lock must be held and must not be in atomic section,
2974d429c5dSJiri Pirko  *	in case SWITCHDEV_F_DEFER flag is not set.
2984d429c5dSJiri Pirko  */
2994d429c5dSJiri Pirko int switchdev_port_obj_del(struct net_device *dev,
3004d429c5dSJiri Pirko 			   const struct switchdev_obj *obj)
3014d429c5dSJiri Pirko {
3024d429c5dSJiri Pirko 	if (obj->flags & SWITCHDEV_F_DEFER)
3034d429c5dSJiri Pirko 		return switchdev_port_obj_del_defer(dev, obj);
3044d429c5dSJiri Pirko 	ASSERT_RTNL();
3054d429c5dSJiri Pirko 	return switchdev_port_obj_del_now(dev, obj);
3064d429c5dSJiri Pirko }
307491d0f15SScott Feldman EXPORT_SYMBOL_GPL(switchdev_port_obj_del);
308491d0f15SScott Feldman 
309ff5cf100SArkadi Sharshevsky static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain);
310a93e3b17SPetr Machata static BLOCKING_NOTIFIER_HEAD(switchdev_blocking_notif_chain);
31103bf0c28SJiri Pirko 
31203bf0c28SJiri Pirko /**
313ebb9a03aSJiri Pirko  *	register_switchdev_notifier - Register notifier
31403bf0c28SJiri Pirko  *	@nb: notifier_block
31503bf0c28SJiri Pirko  *
316ff5cf100SArkadi Sharshevsky  *	Register switch device notifier.
31703bf0c28SJiri Pirko  */
318ebb9a03aSJiri Pirko int register_switchdev_notifier(struct notifier_block *nb)
31903bf0c28SJiri Pirko {
320ff5cf100SArkadi Sharshevsky 	return atomic_notifier_chain_register(&switchdev_notif_chain, nb);
32103bf0c28SJiri Pirko }
322ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(register_switchdev_notifier);
32303bf0c28SJiri Pirko 
32403bf0c28SJiri Pirko /**
325ebb9a03aSJiri Pirko  *	unregister_switchdev_notifier - Unregister notifier
32603bf0c28SJiri Pirko  *	@nb: notifier_block
32703bf0c28SJiri Pirko  *
32803bf0c28SJiri Pirko  *	Unregister switch device notifier.
32903bf0c28SJiri Pirko  */
330ebb9a03aSJiri Pirko int unregister_switchdev_notifier(struct notifier_block *nb)
33103bf0c28SJiri Pirko {
332ff5cf100SArkadi Sharshevsky 	return atomic_notifier_chain_unregister(&switchdev_notif_chain, nb);
33303bf0c28SJiri Pirko }
334ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(unregister_switchdev_notifier);
33503bf0c28SJiri Pirko 
33603bf0c28SJiri Pirko /**
337ebb9a03aSJiri Pirko  *	call_switchdev_notifiers - Call notifiers
33803bf0c28SJiri Pirko  *	@val: value passed unmodified to notifier function
33903bf0c28SJiri Pirko  *	@dev: port device
34003bf0c28SJiri Pirko  *	@info: notifier information data
341ea6754aeSTian Tao  *	@extack: netlink extended ack
342ff5cf100SArkadi Sharshevsky  *	Call all network notifier blocks.
34303bf0c28SJiri Pirko  */
344ebb9a03aSJiri Pirko int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
3456685987cSPetr Machata 			     struct switchdev_notifier_info *info,
3466685987cSPetr Machata 			     struct netlink_ext_ack *extack)
34703bf0c28SJiri Pirko {
34803bf0c28SJiri Pirko 	info->dev = dev;
3496685987cSPetr Machata 	info->extack = extack;
350ff5cf100SArkadi Sharshevsky 	return atomic_notifier_call_chain(&switchdev_notif_chain, val, info);
35103bf0c28SJiri Pirko }
352ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
3538a44dbb2SRoopa Prabhu 
354a93e3b17SPetr Machata int register_switchdev_blocking_notifier(struct notifier_block *nb)
355a93e3b17SPetr Machata {
356a93e3b17SPetr Machata 	struct blocking_notifier_head *chain = &switchdev_blocking_notif_chain;
357a93e3b17SPetr Machata 
358a93e3b17SPetr Machata 	return blocking_notifier_chain_register(chain, nb);
359a93e3b17SPetr Machata }
360a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(register_switchdev_blocking_notifier);
361a93e3b17SPetr Machata 
362a93e3b17SPetr Machata int unregister_switchdev_blocking_notifier(struct notifier_block *nb)
363a93e3b17SPetr Machata {
364a93e3b17SPetr Machata 	struct blocking_notifier_head *chain = &switchdev_blocking_notif_chain;
365a93e3b17SPetr Machata 
366a93e3b17SPetr Machata 	return blocking_notifier_chain_unregister(chain, nb);
367a93e3b17SPetr Machata }
368a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(unregister_switchdev_blocking_notifier);
369a93e3b17SPetr Machata 
370a93e3b17SPetr Machata int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev,
371479c86dcSPetr Machata 				      struct switchdev_notifier_info *info,
372479c86dcSPetr Machata 				      struct netlink_ext_ack *extack)
373a93e3b17SPetr Machata {
374a93e3b17SPetr Machata 	info->dev = dev;
375479c86dcSPetr Machata 	info->extack = extack;
376a93e3b17SPetr Machata 	return blocking_notifier_call_chain(&switchdev_blocking_notif_chain,
377a93e3b17SPetr Machata 					    val, info);
378a93e3b17SPetr Machata }
379a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(call_switchdev_blocking_notifiers);
380a93e3b17SPetr Machata 
381*2b0a5688SVladimir Oltean struct switchdev_nested_priv {
382*2b0a5688SVladimir Oltean 	bool (*check_cb)(const struct net_device *dev);
383*2b0a5688SVladimir Oltean 	bool (*foreign_dev_check_cb)(const struct net_device *dev,
384*2b0a5688SVladimir Oltean 				     const struct net_device *foreign_dev);
385*2b0a5688SVladimir Oltean 	const struct net_device *dev;
386*2b0a5688SVladimir Oltean 	struct net_device *lower_dev;
387*2b0a5688SVladimir Oltean };
388*2b0a5688SVladimir Oltean 
389*2b0a5688SVladimir Oltean static int switchdev_lower_dev_walk(struct net_device *lower_dev,
390*2b0a5688SVladimir Oltean 				    struct netdev_nested_priv *priv)
391*2b0a5688SVladimir Oltean {
392*2b0a5688SVladimir Oltean 	struct switchdev_nested_priv *switchdev_priv = priv->data;
393*2b0a5688SVladimir Oltean 	bool (*foreign_dev_check_cb)(const struct net_device *dev,
394*2b0a5688SVladimir Oltean 				     const struct net_device *foreign_dev);
395*2b0a5688SVladimir Oltean 	bool (*check_cb)(const struct net_device *dev);
396*2b0a5688SVladimir Oltean 	const struct net_device *dev;
397*2b0a5688SVladimir Oltean 
398*2b0a5688SVladimir Oltean 	check_cb = switchdev_priv->check_cb;
399*2b0a5688SVladimir Oltean 	foreign_dev_check_cb = switchdev_priv->foreign_dev_check_cb;
400*2b0a5688SVladimir Oltean 	dev = switchdev_priv->dev;
401*2b0a5688SVladimir Oltean 
402*2b0a5688SVladimir Oltean 	if (check_cb(lower_dev) && !foreign_dev_check_cb(lower_dev, dev)) {
403*2b0a5688SVladimir Oltean 		switchdev_priv->lower_dev = lower_dev;
404*2b0a5688SVladimir Oltean 		return 1;
405*2b0a5688SVladimir Oltean 	}
406*2b0a5688SVladimir Oltean 
407*2b0a5688SVladimir Oltean 	return 0;
408*2b0a5688SVladimir Oltean }
409*2b0a5688SVladimir Oltean 
410*2b0a5688SVladimir Oltean static struct net_device *
411*2b0a5688SVladimir Oltean switchdev_lower_dev_find(struct net_device *dev,
412*2b0a5688SVladimir Oltean 			 bool (*check_cb)(const struct net_device *dev),
413*2b0a5688SVladimir Oltean 			 bool (*foreign_dev_check_cb)(const struct net_device *dev,
414*2b0a5688SVladimir Oltean 						      const struct net_device *foreign_dev))
415*2b0a5688SVladimir Oltean {
416*2b0a5688SVladimir Oltean 	struct switchdev_nested_priv switchdev_priv = {
417*2b0a5688SVladimir Oltean 		.check_cb = check_cb,
418*2b0a5688SVladimir Oltean 		.foreign_dev_check_cb = foreign_dev_check_cb,
419*2b0a5688SVladimir Oltean 		.dev = dev,
420*2b0a5688SVladimir Oltean 		.lower_dev = NULL,
421*2b0a5688SVladimir Oltean 	};
422*2b0a5688SVladimir Oltean 	struct netdev_nested_priv priv = {
423*2b0a5688SVladimir Oltean 		.data = &switchdev_priv,
424*2b0a5688SVladimir Oltean 	};
425*2b0a5688SVladimir Oltean 
426*2b0a5688SVladimir Oltean 	netdev_walk_all_lower_dev_rcu(dev, switchdev_lower_dev_walk, &priv);
427*2b0a5688SVladimir Oltean 
428*2b0a5688SVladimir Oltean 	return switchdev_priv.lower_dev;
429*2b0a5688SVladimir Oltean }
430*2b0a5688SVladimir Oltean 
4318ca07176SVladimir Oltean static int __switchdev_handle_fdb_add_to_device(struct net_device *dev,
4328ca07176SVladimir Oltean 		const struct net_device *orig_dev,
4338ca07176SVladimir Oltean 		const struct switchdev_notifier_fdb_info *fdb_info,
4348ca07176SVladimir Oltean 		bool (*check_cb)(const struct net_device *dev),
4358ca07176SVladimir Oltean 		bool (*foreign_dev_check_cb)(const struct net_device *dev,
4368ca07176SVladimir Oltean 					     const struct net_device *foreign_dev),
4378ca07176SVladimir Oltean 		int (*add_cb)(struct net_device *dev,
4388ca07176SVladimir Oltean 			      const struct net_device *orig_dev, const void *ctx,
4398ca07176SVladimir Oltean 			      const struct switchdev_notifier_fdb_info *fdb_info),
4408ca07176SVladimir Oltean 		int (*lag_add_cb)(struct net_device *dev,
4418ca07176SVladimir Oltean 				  const struct net_device *orig_dev, const void *ctx,
4428ca07176SVladimir Oltean 				  const struct switchdev_notifier_fdb_info *fdb_info))
4438ca07176SVladimir Oltean {
4448ca07176SVladimir Oltean 	const struct switchdev_notifier_info *info = &fdb_info->info;
445*2b0a5688SVladimir Oltean 	struct net_device *br, *lower_dev;
4468ca07176SVladimir Oltean 	struct list_head *iter;
4478ca07176SVladimir Oltean 	int err = -EOPNOTSUPP;
4488ca07176SVladimir Oltean 
449*2b0a5688SVladimir Oltean 	if (check_cb(dev))
4508ca07176SVladimir Oltean 		return add_cb(dev, orig_dev, info->ctx, fdb_info);
4518ca07176SVladimir Oltean 
4528ca07176SVladimir Oltean 	if (netif_is_lag_master(dev)) {
453*2b0a5688SVladimir Oltean 		if (!switchdev_lower_dev_find(dev, check_cb, foreign_dev_check_cb))
454*2b0a5688SVladimir Oltean 			goto maybe_bridged_with_us;
455*2b0a5688SVladimir Oltean 
456*2b0a5688SVladimir Oltean 		/* This is a LAG interface that we offload */
4578ca07176SVladimir Oltean 		if (!lag_add_cb)
4588ca07176SVladimir Oltean 			return -EOPNOTSUPP;
4598ca07176SVladimir Oltean 
4608ca07176SVladimir Oltean 		return lag_add_cb(dev, orig_dev, info->ctx, fdb_info);
4618ca07176SVladimir Oltean 	}
4628ca07176SVladimir Oltean 
4638ca07176SVladimir Oltean 	/* Recurse through lower interfaces in case the FDB entry is pointing
4648ca07176SVladimir Oltean 	 * towards a bridge device.
4658ca07176SVladimir Oltean 	 */
466*2b0a5688SVladimir Oltean 	if (netif_is_bridge_master(dev)) {
467*2b0a5688SVladimir Oltean 		if (!switchdev_lower_dev_find(dev, check_cb, foreign_dev_check_cb))
468*2b0a5688SVladimir Oltean 			return 0;
469*2b0a5688SVladimir Oltean 
470*2b0a5688SVladimir Oltean 		/* This is a bridge interface that we offload */
4718ca07176SVladimir Oltean 		netdev_for_each_lower_dev(dev, lower_dev, iter) {
4728ca07176SVladimir Oltean 			/* Do not propagate FDB entries across bridges */
4738ca07176SVladimir Oltean 			if (netif_is_bridge_master(lower_dev))
4748ca07176SVladimir Oltean 				continue;
4758ca07176SVladimir Oltean 
476*2b0a5688SVladimir Oltean 			/* Bridge ports might be either us, or LAG interfaces
477*2b0a5688SVladimir Oltean 			 * that we offload.
478*2b0a5688SVladimir Oltean 			 */
479*2b0a5688SVladimir Oltean 			if (!check_cb(lower_dev) &&
480*2b0a5688SVladimir Oltean 			    !switchdev_lower_dev_find(lower_dev, check_cb,
481*2b0a5688SVladimir Oltean 						      foreign_dev_check_cb))
482*2b0a5688SVladimir Oltean 				continue;
483*2b0a5688SVladimir Oltean 
4848ca07176SVladimir Oltean 			err = __switchdev_handle_fdb_add_to_device(lower_dev, orig_dev,
4858ca07176SVladimir Oltean 								   fdb_info, check_cb,
4868ca07176SVladimir Oltean 								   foreign_dev_check_cb,
4878ca07176SVladimir Oltean 								   add_cb, lag_add_cb);
4888ca07176SVladimir Oltean 			if (err && err != -EOPNOTSUPP)
4898ca07176SVladimir Oltean 				return err;
4908ca07176SVladimir Oltean 		}
4918ca07176SVladimir Oltean 
492*2b0a5688SVladimir Oltean 		return 0;
493*2b0a5688SVladimir Oltean 	}
494*2b0a5688SVladimir Oltean 
495*2b0a5688SVladimir Oltean maybe_bridged_with_us:
496*2b0a5688SVladimir Oltean 	/* Event is neither on a bridge nor a LAG. Check whether it is on an
497*2b0a5688SVladimir Oltean 	 * interface that is in a bridge with us.
498*2b0a5688SVladimir Oltean 	 */
499*2b0a5688SVladimir Oltean 	br = netdev_master_upper_dev_get_rcu(dev);
500*2b0a5688SVladimir Oltean 	if (!br || !netif_is_bridge_master(br))
501*2b0a5688SVladimir Oltean 		return 0;
502*2b0a5688SVladimir Oltean 
503*2b0a5688SVladimir Oltean 	if (!switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb))
504*2b0a5688SVladimir Oltean 		return 0;
505*2b0a5688SVladimir Oltean 
506*2b0a5688SVladimir Oltean 	return __switchdev_handle_fdb_add_to_device(br, orig_dev, fdb_info,
507*2b0a5688SVladimir Oltean 						    check_cb, foreign_dev_check_cb,
508*2b0a5688SVladimir Oltean 						    add_cb, lag_add_cb);
5098ca07176SVladimir Oltean }
5108ca07176SVladimir Oltean 
5118ca07176SVladimir Oltean int switchdev_handle_fdb_add_to_device(struct net_device *dev,
5128ca07176SVladimir Oltean 		const struct switchdev_notifier_fdb_info *fdb_info,
5138ca07176SVladimir Oltean 		bool (*check_cb)(const struct net_device *dev),
5148ca07176SVladimir Oltean 		bool (*foreign_dev_check_cb)(const struct net_device *dev,
5158ca07176SVladimir Oltean 					     const struct net_device *foreign_dev),
5168ca07176SVladimir Oltean 		int (*add_cb)(struct net_device *dev,
5178ca07176SVladimir Oltean 			      const struct net_device *orig_dev, const void *ctx,
5188ca07176SVladimir Oltean 			      const struct switchdev_notifier_fdb_info *fdb_info),
5198ca07176SVladimir Oltean 		int (*lag_add_cb)(struct net_device *dev,
5208ca07176SVladimir Oltean 				  const struct net_device *orig_dev, const void *ctx,
5218ca07176SVladimir Oltean 				  const struct switchdev_notifier_fdb_info *fdb_info))
5228ca07176SVladimir Oltean {
5238ca07176SVladimir Oltean 	int err;
5248ca07176SVladimir Oltean 
5258ca07176SVladimir Oltean 	err = __switchdev_handle_fdb_add_to_device(dev, dev, fdb_info,
5268ca07176SVladimir Oltean 						   check_cb,
5278ca07176SVladimir Oltean 						   foreign_dev_check_cb,
5288ca07176SVladimir Oltean 						   add_cb, lag_add_cb);
5298ca07176SVladimir Oltean 	if (err == -EOPNOTSUPP)
5308ca07176SVladimir Oltean 		err = 0;
5318ca07176SVladimir Oltean 
5328ca07176SVladimir Oltean 	return err;
5338ca07176SVladimir Oltean }
5348ca07176SVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_handle_fdb_add_to_device);
5358ca07176SVladimir Oltean 
5368ca07176SVladimir Oltean static int __switchdev_handle_fdb_del_to_device(struct net_device *dev,
5378ca07176SVladimir Oltean 		const struct net_device *orig_dev,
5388ca07176SVladimir Oltean 		const struct switchdev_notifier_fdb_info *fdb_info,
5398ca07176SVladimir Oltean 		bool (*check_cb)(const struct net_device *dev),
5408ca07176SVladimir Oltean 		bool (*foreign_dev_check_cb)(const struct net_device *dev,
5418ca07176SVladimir Oltean 					     const struct net_device *foreign_dev),
5428ca07176SVladimir Oltean 		int (*del_cb)(struct net_device *dev,
5438ca07176SVladimir Oltean 			      const struct net_device *orig_dev, const void *ctx,
5448ca07176SVladimir Oltean 			      const struct switchdev_notifier_fdb_info *fdb_info),
5458ca07176SVladimir Oltean 		int (*lag_del_cb)(struct net_device *dev,
5468ca07176SVladimir Oltean 				  const struct net_device *orig_dev, const void *ctx,
5478ca07176SVladimir Oltean 				  const struct switchdev_notifier_fdb_info *fdb_info))
5488ca07176SVladimir Oltean {
5498ca07176SVladimir Oltean 	const struct switchdev_notifier_info *info = &fdb_info->info;
550*2b0a5688SVladimir Oltean 	struct net_device *br, *lower_dev;
5518ca07176SVladimir Oltean 	struct list_head *iter;
5528ca07176SVladimir Oltean 	int err = -EOPNOTSUPP;
5538ca07176SVladimir Oltean 
554*2b0a5688SVladimir Oltean 	if (check_cb(dev))
5558ca07176SVladimir Oltean 		return del_cb(dev, orig_dev, info->ctx, fdb_info);
5568ca07176SVladimir Oltean 
5578ca07176SVladimir Oltean 	if (netif_is_lag_master(dev)) {
558*2b0a5688SVladimir Oltean 		if (!switchdev_lower_dev_find(dev, check_cb, foreign_dev_check_cb))
559*2b0a5688SVladimir Oltean 			goto maybe_bridged_with_us;
560*2b0a5688SVladimir Oltean 
561*2b0a5688SVladimir Oltean 		/* This is a LAG interface that we offload */
5628ca07176SVladimir Oltean 		if (!lag_del_cb)
5638ca07176SVladimir Oltean 			return -EOPNOTSUPP;
5648ca07176SVladimir Oltean 
5658ca07176SVladimir Oltean 		return lag_del_cb(dev, orig_dev, info->ctx, fdb_info);
5668ca07176SVladimir Oltean 	}
5678ca07176SVladimir Oltean 
5688ca07176SVladimir Oltean 	/* Recurse through lower interfaces in case the FDB entry is pointing
5698ca07176SVladimir Oltean 	 * towards a bridge device.
5708ca07176SVladimir Oltean 	 */
571*2b0a5688SVladimir Oltean 	if (netif_is_bridge_master(dev)) {
572*2b0a5688SVladimir Oltean 		if (!switchdev_lower_dev_find(dev, check_cb, foreign_dev_check_cb))
573*2b0a5688SVladimir Oltean 			return 0;
574*2b0a5688SVladimir Oltean 
575*2b0a5688SVladimir Oltean 		/* This is a bridge interface that we offload */
5768ca07176SVladimir Oltean 		netdev_for_each_lower_dev(dev, lower_dev, iter) {
5778ca07176SVladimir Oltean 			/* Do not propagate FDB entries across bridges */
5788ca07176SVladimir Oltean 			if (netif_is_bridge_master(lower_dev))
5798ca07176SVladimir Oltean 				continue;
5808ca07176SVladimir Oltean 
581*2b0a5688SVladimir Oltean 			/* Bridge ports might be either us, or LAG interfaces
582*2b0a5688SVladimir Oltean 			 * that we offload.
583*2b0a5688SVladimir Oltean 			 */
584*2b0a5688SVladimir Oltean 			if (!check_cb(lower_dev) &&
585*2b0a5688SVladimir Oltean 			    !switchdev_lower_dev_find(lower_dev, check_cb,
586*2b0a5688SVladimir Oltean 						      foreign_dev_check_cb))
587*2b0a5688SVladimir Oltean 				continue;
588*2b0a5688SVladimir Oltean 
58971f4f89aSVladimir Oltean 			err = __switchdev_handle_fdb_del_to_device(lower_dev, orig_dev,
59071f4f89aSVladimir Oltean 								   fdb_info, check_cb,
5918ca07176SVladimir Oltean 								   foreign_dev_check_cb,
5928ca07176SVladimir Oltean 								   del_cb, lag_del_cb);
5938ca07176SVladimir Oltean 			if (err && err != -EOPNOTSUPP)
5948ca07176SVladimir Oltean 				return err;
5958ca07176SVladimir Oltean 		}
5968ca07176SVladimir Oltean 
597*2b0a5688SVladimir Oltean 		return 0;
598*2b0a5688SVladimir Oltean 	}
599*2b0a5688SVladimir Oltean 
600*2b0a5688SVladimir Oltean maybe_bridged_with_us:
601*2b0a5688SVladimir Oltean 	/* Event is neither on a bridge nor a LAG. Check whether it is on an
602*2b0a5688SVladimir Oltean 	 * interface that is in a bridge with us.
603*2b0a5688SVladimir Oltean 	 */
604*2b0a5688SVladimir Oltean 	br = netdev_master_upper_dev_get_rcu(dev);
605*2b0a5688SVladimir Oltean 	if (!br || !netif_is_bridge_master(br))
606*2b0a5688SVladimir Oltean 		return 0;
607*2b0a5688SVladimir Oltean 
608*2b0a5688SVladimir Oltean 	if (!switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb))
609*2b0a5688SVladimir Oltean 		return 0;
610*2b0a5688SVladimir Oltean 
611*2b0a5688SVladimir Oltean 	return __switchdev_handle_fdb_del_to_device(br, orig_dev, fdb_info,
612*2b0a5688SVladimir Oltean 						    check_cb, foreign_dev_check_cb,
613*2b0a5688SVladimir Oltean 						    del_cb, lag_del_cb);
6148ca07176SVladimir Oltean }
6158ca07176SVladimir Oltean 
6168ca07176SVladimir Oltean int switchdev_handle_fdb_del_to_device(struct net_device *dev,
6178ca07176SVladimir Oltean 		const struct switchdev_notifier_fdb_info *fdb_info,
6188ca07176SVladimir Oltean 		bool (*check_cb)(const struct net_device *dev),
6198ca07176SVladimir Oltean 		bool (*foreign_dev_check_cb)(const struct net_device *dev,
6208ca07176SVladimir Oltean 					     const struct net_device *foreign_dev),
6218ca07176SVladimir Oltean 		int (*del_cb)(struct net_device *dev,
6228ca07176SVladimir Oltean 			      const struct net_device *orig_dev, const void *ctx,
6238ca07176SVladimir Oltean 			      const struct switchdev_notifier_fdb_info *fdb_info),
6248ca07176SVladimir Oltean 		int (*lag_del_cb)(struct net_device *dev,
6258ca07176SVladimir Oltean 				  const struct net_device *orig_dev, const void *ctx,
6268ca07176SVladimir Oltean 				  const struct switchdev_notifier_fdb_info *fdb_info))
6278ca07176SVladimir Oltean {
6288ca07176SVladimir Oltean 	int err;
6298ca07176SVladimir Oltean 
6308ca07176SVladimir Oltean 	err = __switchdev_handle_fdb_del_to_device(dev, dev, fdb_info,
6318ca07176SVladimir Oltean 						   check_cb,
6328ca07176SVladimir Oltean 						   foreign_dev_check_cb,
6338ca07176SVladimir Oltean 						   del_cb, lag_del_cb);
6348ca07176SVladimir Oltean 	if (err == -EOPNOTSUPP)
6358ca07176SVladimir Oltean 		err = 0;
6368ca07176SVladimir Oltean 
6378ca07176SVladimir Oltean 	return err;
6388ca07176SVladimir Oltean }
6398ca07176SVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_handle_fdb_del_to_device);
6408ca07176SVladimir Oltean 
641f30f0601SPetr Machata static int __switchdev_handle_port_obj_add(struct net_device *dev,
642f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
643f30f0601SPetr Machata 			bool (*check_cb)(const struct net_device *dev),
64469bfac96SVladimir Oltean 			int (*add_cb)(struct net_device *dev, const void *ctx,
645f30f0601SPetr Machata 				      const struct switchdev_obj *obj,
64669213513SPetr Machata 				      struct netlink_ext_ack *extack))
647f30f0601SPetr Machata {
64869bfac96SVladimir Oltean 	struct switchdev_notifier_info *info = &port_obj_info->info;
64969213513SPetr Machata 	struct netlink_ext_ack *extack;
650f30f0601SPetr Machata 	struct net_device *lower_dev;
651f30f0601SPetr Machata 	struct list_head *iter;
652f30f0601SPetr Machata 	int err = -EOPNOTSUPP;
653f30f0601SPetr Machata 
65469bfac96SVladimir Oltean 	extack = switchdev_notifier_info_to_extack(info);
65569213513SPetr Machata 
656f30f0601SPetr Machata 	if (check_cb(dev)) {
65769bfac96SVladimir Oltean 		err = add_cb(dev, info->ctx, port_obj_info->obj, extack);
65820776b46SRasmus Villemoes 		if (err != -EOPNOTSUPP)
659f30f0601SPetr Machata 			port_obj_info->handled = true;
66020776b46SRasmus Villemoes 		return err;
661f30f0601SPetr Machata 	}
662f30f0601SPetr Machata 
663f30f0601SPetr Machata 	/* Switch ports might be stacked under e.g. a LAG. Ignore the
664f30f0601SPetr Machata 	 * unsupported devices, another driver might be able to handle them. But
665f30f0601SPetr Machata 	 * propagate to the callers any hard errors.
666f30f0601SPetr Machata 	 *
667f30f0601SPetr Machata 	 * If the driver does its own bookkeeping of stacked ports, it's not
668f30f0601SPetr Machata 	 * necessary to go through this helper.
669f30f0601SPetr Machata 	 */
670f30f0601SPetr Machata 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
67107c6f980SRussell King 		if (netif_is_bridge_master(lower_dev))
67207c6f980SRussell King 			continue;
67307c6f980SRussell King 
674f30f0601SPetr Machata 		err = __switchdev_handle_port_obj_add(lower_dev, port_obj_info,
675f30f0601SPetr Machata 						      check_cb, add_cb);
676f30f0601SPetr Machata 		if (err && err != -EOPNOTSUPP)
677f30f0601SPetr Machata 			return err;
678f30f0601SPetr Machata 	}
679f30f0601SPetr Machata 
680f30f0601SPetr Machata 	return err;
681f30f0601SPetr Machata }
682f30f0601SPetr Machata 
683f30f0601SPetr Machata int switchdev_handle_port_obj_add(struct net_device *dev,
684f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
685f30f0601SPetr Machata 			bool (*check_cb)(const struct net_device *dev),
68669bfac96SVladimir Oltean 			int (*add_cb)(struct net_device *dev, const void *ctx,
687f30f0601SPetr Machata 				      const struct switchdev_obj *obj,
68869213513SPetr Machata 				      struct netlink_ext_ack *extack))
689f30f0601SPetr Machata {
690f30f0601SPetr Machata 	int err;
691f30f0601SPetr Machata 
692f30f0601SPetr Machata 	err = __switchdev_handle_port_obj_add(dev, port_obj_info, check_cb,
693f30f0601SPetr Machata 					      add_cb);
694f30f0601SPetr Machata 	if (err == -EOPNOTSUPP)
695f30f0601SPetr Machata 		err = 0;
696f30f0601SPetr Machata 	return err;
697f30f0601SPetr Machata }
698f30f0601SPetr Machata EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add);
699f30f0601SPetr Machata 
700f30f0601SPetr Machata static int __switchdev_handle_port_obj_del(struct net_device *dev,
701f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
702f30f0601SPetr Machata 			bool (*check_cb)(const struct net_device *dev),
70369bfac96SVladimir Oltean 			int (*del_cb)(struct net_device *dev, const void *ctx,
704f30f0601SPetr Machata 				      const struct switchdev_obj *obj))
705f30f0601SPetr Machata {
70669bfac96SVladimir Oltean 	struct switchdev_notifier_info *info = &port_obj_info->info;
707f30f0601SPetr Machata 	struct net_device *lower_dev;
708f30f0601SPetr Machata 	struct list_head *iter;
709f30f0601SPetr Machata 	int err = -EOPNOTSUPP;
710f30f0601SPetr Machata 
711f30f0601SPetr Machata 	if (check_cb(dev)) {
71269bfac96SVladimir Oltean 		err = del_cb(dev, info->ctx, port_obj_info->obj);
71320776b46SRasmus Villemoes 		if (err != -EOPNOTSUPP)
714f30f0601SPetr Machata 			port_obj_info->handled = true;
71520776b46SRasmus Villemoes 		return err;
716f30f0601SPetr Machata 	}
717f30f0601SPetr Machata 
718f30f0601SPetr Machata 	/* Switch ports might be stacked under e.g. a LAG. Ignore the
719f30f0601SPetr Machata 	 * unsupported devices, another driver might be able to handle them. But
720f30f0601SPetr Machata 	 * propagate to the callers any hard errors.
721f30f0601SPetr Machata 	 *
722f30f0601SPetr Machata 	 * If the driver does its own bookkeeping of stacked ports, it's not
723f30f0601SPetr Machata 	 * necessary to go through this helper.
724f30f0601SPetr Machata 	 */
725f30f0601SPetr Machata 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
72607c6f980SRussell King 		if (netif_is_bridge_master(lower_dev))
72707c6f980SRussell King 			continue;
72807c6f980SRussell King 
729f30f0601SPetr Machata 		err = __switchdev_handle_port_obj_del(lower_dev, port_obj_info,
730f30f0601SPetr Machata 						      check_cb, del_cb);
731f30f0601SPetr Machata 		if (err && err != -EOPNOTSUPP)
732f30f0601SPetr Machata 			return err;
733f30f0601SPetr Machata 	}
734f30f0601SPetr Machata 
735f30f0601SPetr Machata 	return err;
736f30f0601SPetr Machata }
737f30f0601SPetr Machata 
738f30f0601SPetr Machata int switchdev_handle_port_obj_del(struct net_device *dev,
739f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
740f30f0601SPetr Machata 			bool (*check_cb)(const struct net_device *dev),
74169bfac96SVladimir Oltean 			int (*del_cb)(struct net_device *dev, const void *ctx,
742f30f0601SPetr Machata 				      const struct switchdev_obj *obj))
743f30f0601SPetr Machata {
744f30f0601SPetr Machata 	int err;
745f30f0601SPetr Machata 
746f30f0601SPetr Machata 	err = __switchdev_handle_port_obj_del(dev, port_obj_info, check_cb,
747f30f0601SPetr Machata 					      del_cb);
748f30f0601SPetr Machata 	if (err == -EOPNOTSUPP)
749f30f0601SPetr Machata 		err = 0;
750f30f0601SPetr Machata 	return err;
751f30f0601SPetr Machata }
752f30f0601SPetr Machata EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del);
7531cb33af1SFlorian Fainelli 
7541cb33af1SFlorian Fainelli static int __switchdev_handle_port_attr_set(struct net_device *dev,
7551cb33af1SFlorian Fainelli 			struct switchdev_notifier_port_attr_info *port_attr_info,
7561cb33af1SFlorian Fainelli 			bool (*check_cb)(const struct net_device *dev),
75769bfac96SVladimir Oltean 			int (*set_cb)(struct net_device *dev, const void *ctx,
7584c08c586SVladimir Oltean 				      const struct switchdev_attr *attr,
7594c08c586SVladimir Oltean 				      struct netlink_ext_ack *extack))
7601cb33af1SFlorian Fainelli {
76169bfac96SVladimir Oltean 	struct switchdev_notifier_info *info = &port_attr_info->info;
7624c08c586SVladimir Oltean 	struct netlink_ext_ack *extack;
7631cb33af1SFlorian Fainelli 	struct net_device *lower_dev;
7641cb33af1SFlorian Fainelli 	struct list_head *iter;
7651cb33af1SFlorian Fainelli 	int err = -EOPNOTSUPP;
7661cb33af1SFlorian Fainelli 
76769bfac96SVladimir Oltean 	extack = switchdev_notifier_info_to_extack(info);
7684c08c586SVladimir Oltean 
7691cb33af1SFlorian Fainelli 	if (check_cb(dev)) {
77069bfac96SVladimir Oltean 		err = set_cb(dev, info->ctx, port_attr_info->attr, extack);
77120776b46SRasmus Villemoes 		if (err != -EOPNOTSUPP)
7721cb33af1SFlorian Fainelli 			port_attr_info->handled = true;
77320776b46SRasmus Villemoes 		return err;
7741cb33af1SFlorian Fainelli 	}
7751cb33af1SFlorian Fainelli 
7761cb33af1SFlorian Fainelli 	/* Switch ports might be stacked under e.g. a LAG. Ignore the
7771cb33af1SFlorian Fainelli 	 * unsupported devices, another driver might be able to handle them. But
7781cb33af1SFlorian Fainelli 	 * propagate to the callers any hard errors.
7791cb33af1SFlorian Fainelli 	 *
7801cb33af1SFlorian Fainelli 	 * If the driver does its own bookkeeping of stacked ports, it's not
7811cb33af1SFlorian Fainelli 	 * necessary to go through this helper.
7821cb33af1SFlorian Fainelli 	 */
7831cb33af1SFlorian Fainelli 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
78407c6f980SRussell King 		if (netif_is_bridge_master(lower_dev))
78507c6f980SRussell King 			continue;
78607c6f980SRussell King 
7871cb33af1SFlorian Fainelli 		err = __switchdev_handle_port_attr_set(lower_dev, port_attr_info,
7881cb33af1SFlorian Fainelli 						       check_cb, set_cb);
7891cb33af1SFlorian Fainelli 		if (err && err != -EOPNOTSUPP)
7901cb33af1SFlorian Fainelli 			return err;
7911cb33af1SFlorian Fainelli 	}
7921cb33af1SFlorian Fainelli 
7931cb33af1SFlorian Fainelli 	return err;
7941cb33af1SFlorian Fainelli }
7951cb33af1SFlorian Fainelli 
7961cb33af1SFlorian Fainelli int switchdev_handle_port_attr_set(struct net_device *dev,
7971cb33af1SFlorian Fainelli 			struct switchdev_notifier_port_attr_info *port_attr_info,
7981cb33af1SFlorian Fainelli 			bool (*check_cb)(const struct net_device *dev),
79969bfac96SVladimir Oltean 			int (*set_cb)(struct net_device *dev, const void *ctx,
8004c08c586SVladimir Oltean 				      const struct switchdev_attr *attr,
8014c08c586SVladimir Oltean 				      struct netlink_ext_ack *extack))
8021cb33af1SFlorian Fainelli {
8031cb33af1SFlorian Fainelli 	int err;
8041cb33af1SFlorian Fainelli 
8051cb33af1SFlorian Fainelli 	err = __switchdev_handle_port_attr_set(dev, port_attr_info, check_cb,
8061cb33af1SFlorian Fainelli 					       set_cb);
8071cb33af1SFlorian Fainelli 	if (err == -EOPNOTSUPP)
8081cb33af1SFlorian Fainelli 		err = 0;
8091cb33af1SFlorian Fainelli 	return err;
8101cb33af1SFlorian Fainelli }
8111cb33af1SFlorian Fainelli EXPORT_SYMBOL_GPL(switchdev_handle_port_attr_set);
812