xref: /linux-6.15/net/switchdev/switchdev.c (revision c4076cdd)
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;
314fc003feSEric Dumazet 	netdevice_tracker dev_tracker;
32793f4014SJiri Pirko 	switchdev_deferred_func_t *func;
33fbfc8502SGustavo A. R. Silva 	unsigned long data[];
34793f4014SJiri Pirko };
35793f4014SJiri Pirko 
36793f4014SJiri Pirko static struct switchdev_deferred_item *switchdev_deferred_dequeue(void)
37793f4014SJiri Pirko {
38793f4014SJiri Pirko 	struct switchdev_deferred_item *dfitem;
39793f4014SJiri Pirko 
40793f4014SJiri Pirko 	spin_lock_bh(&deferred_lock);
41793f4014SJiri Pirko 	if (list_empty(&deferred)) {
42793f4014SJiri Pirko 		dfitem = NULL;
43793f4014SJiri Pirko 		goto unlock;
44793f4014SJiri Pirko 	}
45793f4014SJiri Pirko 	dfitem = list_first_entry(&deferred,
46793f4014SJiri Pirko 				  struct switchdev_deferred_item, list);
47793f4014SJiri Pirko 	list_del(&dfitem->list);
48793f4014SJiri Pirko unlock:
49793f4014SJiri Pirko 	spin_unlock_bh(&deferred_lock);
50793f4014SJiri Pirko 	return dfitem;
51793f4014SJiri Pirko }
52793f4014SJiri Pirko 
53793f4014SJiri Pirko /**
54793f4014SJiri Pirko  *	switchdev_deferred_process - Process ops in deferred queue
55793f4014SJiri Pirko  *
56793f4014SJiri Pirko  *	Called to flush the ops currently queued in deferred ops queue.
57793f4014SJiri Pirko  *	rtnl_lock must be held.
58793f4014SJiri Pirko  */
59793f4014SJiri Pirko void switchdev_deferred_process(void)
60793f4014SJiri Pirko {
61793f4014SJiri Pirko 	struct switchdev_deferred_item *dfitem;
62793f4014SJiri Pirko 
63793f4014SJiri Pirko 	ASSERT_RTNL();
64793f4014SJiri Pirko 
65793f4014SJiri Pirko 	while ((dfitem = switchdev_deferred_dequeue())) {
66793f4014SJiri Pirko 		dfitem->func(dfitem->dev, dfitem->data);
674fc003feSEric Dumazet 		dev_put_track(dfitem->dev, &dfitem->dev_tracker);
68793f4014SJiri Pirko 		kfree(dfitem);
69793f4014SJiri Pirko 	}
70793f4014SJiri Pirko }
71793f4014SJiri Pirko EXPORT_SYMBOL_GPL(switchdev_deferred_process);
72793f4014SJiri Pirko 
73793f4014SJiri Pirko static void switchdev_deferred_process_work(struct work_struct *work)
74793f4014SJiri Pirko {
75793f4014SJiri Pirko 	rtnl_lock();
76793f4014SJiri Pirko 	switchdev_deferred_process();
77793f4014SJiri Pirko 	rtnl_unlock();
78793f4014SJiri Pirko }
79793f4014SJiri Pirko 
80793f4014SJiri Pirko static DECLARE_WORK(deferred_process_work, switchdev_deferred_process_work);
81793f4014SJiri Pirko 
82793f4014SJiri Pirko static int switchdev_deferred_enqueue(struct net_device *dev,
83793f4014SJiri Pirko 				      const void *data, size_t data_len,
84793f4014SJiri Pirko 				      switchdev_deferred_func_t *func)
85793f4014SJiri Pirko {
86793f4014SJiri Pirko 	struct switchdev_deferred_item *dfitem;
87793f4014SJiri Pirko 
88d8c28581SMinghao Chi (CGEL ZTE) 	dfitem = kmalloc(struct_size(dfitem, data, data_len), GFP_ATOMIC);
89793f4014SJiri Pirko 	if (!dfitem)
90793f4014SJiri Pirko 		return -ENOMEM;
91793f4014SJiri Pirko 	dfitem->dev = dev;
92793f4014SJiri Pirko 	dfitem->func = func;
93793f4014SJiri Pirko 	memcpy(dfitem->data, data, data_len);
944fc003feSEric Dumazet 	dev_hold_track(dev, &dfitem->dev_tracker, GFP_ATOMIC);
95793f4014SJiri Pirko 	spin_lock_bh(&deferred_lock);
96793f4014SJiri Pirko 	list_add_tail(&dfitem->list, &deferred);
97793f4014SJiri Pirko 	spin_unlock_bh(&deferred_lock);
98793f4014SJiri Pirko 	schedule_work(&deferred_process_work);
99793f4014SJiri Pirko 	return 0;
100793f4014SJiri Pirko }
101793f4014SJiri Pirko 
102d45224d6SFlorian Fainelli static int switchdev_port_attr_notify(enum switchdev_notifier_type nt,
103d45224d6SFlorian Fainelli 				      struct net_device *dev,
104dcbdf135SVladimir Oltean 				      const struct switchdev_attr *attr,
105dcbdf135SVladimir Oltean 				      struct netlink_ext_ack *extack)
1063094333dSScott Feldman {
107d45224d6SFlorian Fainelli 	int err;
108d45224d6SFlorian Fainelli 	int rc;
1093094333dSScott Feldman 
110d45224d6SFlorian Fainelli 	struct switchdev_notifier_port_attr_info attr_info = {
111d45224d6SFlorian Fainelli 		.attr = attr,
112d45224d6SFlorian Fainelli 		.handled = false,
113d45224d6SFlorian Fainelli 	};
1143094333dSScott Feldman 
115d45224d6SFlorian Fainelli 	rc = call_switchdev_blocking_notifiers(nt, dev,
116dcbdf135SVladimir Oltean 					       &attr_info.info, extack);
117d45224d6SFlorian Fainelli 	err = notifier_to_errno(rc);
118d45224d6SFlorian Fainelli 	if (err) {
119d45224d6SFlorian Fainelli 		WARN_ON(!attr_info.handled);
1203094333dSScott Feldman 		return err;
1213094333dSScott Feldman 	}
1223094333dSScott Feldman 
123d45224d6SFlorian Fainelli 	if (!attr_info.handled)
124d45224d6SFlorian Fainelli 		return -EOPNOTSUPP;
125d45224d6SFlorian Fainelli 
126d45224d6SFlorian Fainelli 	return 0;
127d45224d6SFlorian Fainelli }
128d45224d6SFlorian Fainelli 
1290bc05d58SJiri Pirko static int switchdev_port_attr_set_now(struct net_device *dev,
130dcbdf135SVladimir Oltean 				       const struct switchdev_attr *attr,
131dcbdf135SVladimir Oltean 				       struct netlink_ext_ack *extack)
1323094333dSScott Feldman {
133dcbdf135SVladimir Oltean 	return switchdev_port_attr_notify(SWITCHDEV_PORT_ATTR_SET, dev, attr,
134dcbdf135SVladimir Oltean 					  extack);
1353094333dSScott Feldman }
1360bc05d58SJiri Pirko 
1370bc05d58SJiri Pirko static void switchdev_port_attr_set_deferred(struct net_device *dev,
1380bc05d58SJiri Pirko 					     const void *data)
1390bc05d58SJiri Pirko {
1400bc05d58SJiri Pirko 	const struct switchdev_attr *attr = data;
1410bc05d58SJiri Pirko 	int err;
1420bc05d58SJiri Pirko 
143dcbdf135SVladimir Oltean 	err = switchdev_port_attr_set_now(dev, attr, NULL);
1440bc05d58SJiri Pirko 	if (err && err != -EOPNOTSUPP)
1450bc05d58SJiri Pirko 		netdev_err(dev, "failed (err=%d) to set attribute (id=%d)\n",
1460bc05d58SJiri Pirko 			   err, attr->id);
1477ceb2afbSElad Raz 	if (attr->complete)
1487ceb2afbSElad Raz 		attr->complete(dev, err, attr->complete_priv);
1490bc05d58SJiri Pirko }
1500bc05d58SJiri Pirko 
1510bc05d58SJiri Pirko static int switchdev_port_attr_set_defer(struct net_device *dev,
1520bc05d58SJiri Pirko 					 const struct switchdev_attr *attr)
1530bc05d58SJiri Pirko {
1540bc05d58SJiri Pirko 	return switchdev_deferred_enqueue(dev, attr, sizeof(*attr),
1550bc05d58SJiri Pirko 					  switchdev_port_attr_set_deferred);
1560bc05d58SJiri Pirko }
1570bc05d58SJiri Pirko 
1580bc05d58SJiri Pirko /**
1590bc05d58SJiri Pirko  *	switchdev_port_attr_set - Set port attribute
1600bc05d58SJiri Pirko  *
1610bc05d58SJiri Pirko  *	@dev: port device
1620bc05d58SJiri Pirko  *	@attr: attribute to set
163dcbdf135SVladimir Oltean  *	@extack: netlink extended ack, for error message propagation
1640bc05d58SJiri Pirko  *
1650bc05d58SJiri Pirko  *	rtnl_lock must be held and must not be in atomic section,
1660bc05d58SJiri Pirko  *	in case SWITCHDEV_F_DEFER flag is not set.
1670bc05d58SJiri Pirko  */
1680bc05d58SJiri Pirko int switchdev_port_attr_set(struct net_device *dev,
169dcbdf135SVladimir Oltean 			    const struct switchdev_attr *attr,
170dcbdf135SVladimir Oltean 			    struct netlink_ext_ack *extack)
1710bc05d58SJiri Pirko {
1720bc05d58SJiri Pirko 	if (attr->flags & SWITCHDEV_F_DEFER)
1730bc05d58SJiri Pirko 		return switchdev_port_attr_set_defer(dev, attr);
1740bc05d58SJiri Pirko 	ASSERT_RTNL();
175dcbdf135SVladimir Oltean 	return switchdev_port_attr_set_now(dev, attr, extack);
1760bc05d58SJiri Pirko }
1773094333dSScott Feldman EXPORT_SYMBOL_GPL(switchdev_port_attr_set);
1783094333dSScott Feldman 
179e258d919SScott Feldman static size_t switchdev_obj_size(const struct switchdev_obj *obj)
180e258d919SScott Feldman {
181e258d919SScott Feldman 	switch (obj->id) {
182e258d919SScott Feldman 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
183e258d919SScott Feldman 		return sizeof(struct switchdev_obj_port_vlan);
1844d41e125SElad Raz 	case SWITCHDEV_OBJ_ID_PORT_MDB:
1854d41e125SElad Raz 		return sizeof(struct switchdev_obj_port_mdb);
18647d5b6dbSAndrew Lunn 	case SWITCHDEV_OBJ_ID_HOST_MDB:
18747d5b6dbSAndrew Lunn 		return sizeof(struct switchdev_obj_port_mdb);
188e258d919SScott Feldman 	default:
189e258d919SScott Feldman 		BUG();
190e258d919SScott Feldman 	}
191e258d919SScott Feldman 	return 0;
192e258d919SScott Feldman }
193e258d919SScott Feldman 
194d17d9f5eSPetr Machata static int switchdev_port_obj_notify(enum switchdev_notifier_type nt,
195d17d9f5eSPetr Machata 				     struct net_device *dev,
196648b4a99SJiri Pirko 				     const struct switchdev_obj *obj,
19769b7320eSPetr Machata 				     struct netlink_ext_ack *extack)
198491d0f15SScott Feldman {
199d17d9f5eSPetr Machata 	int rc;
200d17d9f5eSPetr Machata 	int err;
201491d0f15SScott Feldman 
202d17d9f5eSPetr Machata 	struct switchdev_notifier_port_obj_info obj_info = {
203d17d9f5eSPetr Machata 		.obj = obj,
204d17d9f5eSPetr Machata 		.handled = false,
205d17d9f5eSPetr Machata 	};
206491d0f15SScott Feldman 
207479c86dcSPetr Machata 	rc = call_switchdev_blocking_notifiers(nt, dev, &obj_info.info, extack);
208d17d9f5eSPetr Machata 	err = notifier_to_errno(rc);
209d17d9f5eSPetr Machata 	if (err) {
210d17d9f5eSPetr Machata 		WARN_ON(!obj_info.handled);
211491d0f15SScott Feldman 		return err;
212491d0f15SScott Feldman 	}
213d17d9f5eSPetr Machata 	if (!obj_info.handled)
214d17d9f5eSPetr Machata 		return -EOPNOTSUPP;
215d17d9f5eSPetr Machata 	return 0;
216d17d9f5eSPetr Machata }
217491d0f15SScott Feldman 
2184d429c5dSJiri Pirko static void switchdev_port_obj_add_deferred(struct net_device *dev,
2194d429c5dSJiri Pirko 					    const void *data)
2204d429c5dSJiri Pirko {
2214d429c5dSJiri Pirko 	const struct switchdev_obj *obj = data;
2224d429c5dSJiri Pirko 	int err;
2234d429c5dSJiri Pirko 
224cf6def51SVladimir Oltean 	ASSERT_RTNL();
225cf6def51SVladimir Oltean 	err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD,
226cf6def51SVladimir Oltean 					dev, obj, NULL);
2274d429c5dSJiri Pirko 	if (err && err != -EOPNOTSUPP)
2284d429c5dSJiri Pirko 		netdev_err(dev, "failed (err=%d) to add object (id=%d)\n",
2294d429c5dSJiri Pirko 			   err, obj->id);
2307ceb2afbSElad Raz 	if (obj->complete)
2317ceb2afbSElad Raz 		obj->complete(dev, err, obj->complete_priv);
2324d429c5dSJiri Pirko }
2334d429c5dSJiri Pirko 
2344d429c5dSJiri Pirko static int switchdev_port_obj_add_defer(struct net_device *dev,
2354d429c5dSJiri Pirko 					const struct switchdev_obj *obj)
2364d429c5dSJiri Pirko {
237e258d919SScott Feldman 	return switchdev_deferred_enqueue(dev, obj, switchdev_obj_size(obj),
2384d429c5dSJiri Pirko 					  switchdev_port_obj_add_deferred);
2394d429c5dSJiri Pirko }
240491d0f15SScott Feldman 
241491d0f15SScott Feldman /**
2424d429c5dSJiri Pirko  *	switchdev_port_obj_add - Add port object
243491d0f15SScott Feldman  *
244491d0f15SScott Feldman  *	@dev: port device
2454d429c5dSJiri Pirko  *	@obj: object to add
246c8af73f0SAndrew Lunn  *	@extack: netlink extended ack
2474d429c5dSJiri Pirko  *
2484d429c5dSJiri Pirko  *	rtnl_lock must be held and must not be in atomic section,
2494d429c5dSJiri Pirko  *	in case SWITCHDEV_F_DEFER flag is not set.
250491d0f15SScott Feldman  */
2514d429c5dSJiri Pirko int switchdev_port_obj_add(struct net_device *dev,
25269b7320eSPetr Machata 			   const struct switchdev_obj *obj,
25369b7320eSPetr Machata 			   struct netlink_ext_ack *extack)
2544d429c5dSJiri Pirko {
2554d429c5dSJiri Pirko 	if (obj->flags & SWITCHDEV_F_DEFER)
2564d429c5dSJiri Pirko 		return switchdev_port_obj_add_defer(dev, obj);
2574d429c5dSJiri Pirko 	ASSERT_RTNL();
258cf6def51SVladimir Oltean 	return switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD,
259cf6def51SVladimir Oltean 					 dev, obj, extack);
2604d429c5dSJiri Pirko }
2614d429c5dSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_port_obj_add);
2624d429c5dSJiri Pirko 
2634d429c5dSJiri Pirko static int switchdev_port_obj_del_now(struct net_device *dev,
264648b4a99SJiri Pirko 				      const struct switchdev_obj *obj)
265491d0f15SScott Feldman {
266d17d9f5eSPetr Machata 	return switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_DEL,
267ffb68fc5SVladimir Oltean 					 dev, obj, NULL);
268491d0f15SScott Feldman }
2694d429c5dSJiri Pirko 
2704d429c5dSJiri Pirko static void switchdev_port_obj_del_deferred(struct net_device *dev,
2714d429c5dSJiri Pirko 					    const void *data)
2724d429c5dSJiri Pirko {
2734d429c5dSJiri Pirko 	const struct switchdev_obj *obj = data;
2744d429c5dSJiri Pirko 	int err;
2754d429c5dSJiri Pirko 
2764d429c5dSJiri Pirko 	err = switchdev_port_obj_del_now(dev, obj);
2774d429c5dSJiri Pirko 	if (err && err != -EOPNOTSUPP)
2784d429c5dSJiri Pirko 		netdev_err(dev, "failed (err=%d) to del object (id=%d)\n",
2794d429c5dSJiri Pirko 			   err, obj->id);
2807ceb2afbSElad Raz 	if (obj->complete)
2817ceb2afbSElad Raz 		obj->complete(dev, err, obj->complete_priv);
2824d429c5dSJiri Pirko }
2834d429c5dSJiri Pirko 
2844d429c5dSJiri Pirko static int switchdev_port_obj_del_defer(struct net_device *dev,
2854d429c5dSJiri Pirko 					const struct switchdev_obj *obj)
2864d429c5dSJiri Pirko {
287e258d919SScott Feldman 	return switchdev_deferred_enqueue(dev, obj, switchdev_obj_size(obj),
2884d429c5dSJiri Pirko 					  switchdev_port_obj_del_deferred);
2894d429c5dSJiri Pirko }
2904d429c5dSJiri Pirko 
2914d429c5dSJiri Pirko /**
2924d429c5dSJiri Pirko  *	switchdev_port_obj_del - Delete port object
2934d429c5dSJiri Pirko  *
2944d429c5dSJiri Pirko  *	@dev: port device
2954d429c5dSJiri Pirko  *	@obj: object to delete
2964d429c5dSJiri Pirko  *
2974d429c5dSJiri Pirko  *	rtnl_lock must be held and must not be in atomic section,
2984d429c5dSJiri Pirko  *	in case SWITCHDEV_F_DEFER flag is not set.
2994d429c5dSJiri Pirko  */
3004d429c5dSJiri Pirko int switchdev_port_obj_del(struct net_device *dev,
3014d429c5dSJiri Pirko 			   const struct switchdev_obj *obj)
3024d429c5dSJiri Pirko {
3034d429c5dSJiri Pirko 	if (obj->flags & SWITCHDEV_F_DEFER)
3044d429c5dSJiri Pirko 		return switchdev_port_obj_del_defer(dev, obj);
3054d429c5dSJiri Pirko 	ASSERT_RTNL();
3064d429c5dSJiri Pirko 	return switchdev_port_obj_del_now(dev, obj);
3074d429c5dSJiri Pirko }
308491d0f15SScott Feldman EXPORT_SYMBOL_GPL(switchdev_port_obj_del);
309491d0f15SScott Feldman 
310ff5cf100SArkadi Sharshevsky static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain);
311a93e3b17SPetr Machata static BLOCKING_NOTIFIER_HEAD(switchdev_blocking_notif_chain);
31203bf0c28SJiri Pirko 
31303bf0c28SJiri Pirko /**
314ebb9a03aSJiri Pirko  *	register_switchdev_notifier - Register notifier
31503bf0c28SJiri Pirko  *	@nb: notifier_block
31603bf0c28SJiri Pirko  *
317ff5cf100SArkadi Sharshevsky  *	Register switch device notifier.
31803bf0c28SJiri Pirko  */
319ebb9a03aSJiri Pirko int register_switchdev_notifier(struct notifier_block *nb)
32003bf0c28SJiri Pirko {
321ff5cf100SArkadi Sharshevsky 	return atomic_notifier_chain_register(&switchdev_notif_chain, nb);
32203bf0c28SJiri Pirko }
323ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(register_switchdev_notifier);
32403bf0c28SJiri Pirko 
32503bf0c28SJiri Pirko /**
326ebb9a03aSJiri Pirko  *	unregister_switchdev_notifier - Unregister notifier
32703bf0c28SJiri Pirko  *	@nb: notifier_block
32803bf0c28SJiri Pirko  *
32903bf0c28SJiri Pirko  *	Unregister switch device notifier.
33003bf0c28SJiri Pirko  */
331ebb9a03aSJiri Pirko int unregister_switchdev_notifier(struct notifier_block *nb)
33203bf0c28SJiri Pirko {
333ff5cf100SArkadi Sharshevsky 	return atomic_notifier_chain_unregister(&switchdev_notif_chain, nb);
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
342ea6754aeSTian Tao  *	@extack: netlink extended ack
343ff5cf100SArkadi Sharshevsky  *	Call all network notifier blocks.
34403bf0c28SJiri Pirko  */
345ebb9a03aSJiri Pirko int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
3466685987cSPetr Machata 			     struct switchdev_notifier_info *info,
3476685987cSPetr Machata 			     struct netlink_ext_ack *extack)
34803bf0c28SJiri Pirko {
34903bf0c28SJiri Pirko 	info->dev = dev;
3506685987cSPetr Machata 	info->extack = extack;
351ff5cf100SArkadi Sharshevsky 	return atomic_notifier_call_chain(&switchdev_notif_chain, val, info);
35203bf0c28SJiri Pirko }
353ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
3548a44dbb2SRoopa Prabhu 
355a93e3b17SPetr Machata int register_switchdev_blocking_notifier(struct notifier_block *nb)
356a93e3b17SPetr Machata {
357a93e3b17SPetr Machata 	struct blocking_notifier_head *chain = &switchdev_blocking_notif_chain;
358a93e3b17SPetr Machata 
359a93e3b17SPetr Machata 	return blocking_notifier_chain_register(chain, nb);
360a93e3b17SPetr Machata }
361a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(register_switchdev_blocking_notifier);
362a93e3b17SPetr Machata 
363a93e3b17SPetr Machata int unregister_switchdev_blocking_notifier(struct notifier_block *nb)
364a93e3b17SPetr Machata {
365a93e3b17SPetr Machata 	struct blocking_notifier_head *chain = &switchdev_blocking_notif_chain;
366a93e3b17SPetr Machata 
367a93e3b17SPetr Machata 	return blocking_notifier_chain_unregister(chain, nb);
368a93e3b17SPetr Machata }
369a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(unregister_switchdev_blocking_notifier);
370a93e3b17SPetr Machata 
371a93e3b17SPetr Machata int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev,
372479c86dcSPetr Machata 				      struct switchdev_notifier_info *info,
373479c86dcSPetr Machata 				      struct netlink_ext_ack *extack)
374a93e3b17SPetr Machata {
375a93e3b17SPetr Machata 	info->dev = dev;
376479c86dcSPetr Machata 	info->extack = extack;
377a93e3b17SPetr Machata 	return blocking_notifier_call_chain(&switchdev_blocking_notif_chain,
378a93e3b17SPetr Machata 					    val, info);
379a93e3b17SPetr Machata }
380a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(call_switchdev_blocking_notifiers);
381a93e3b17SPetr Machata 
3822b0a5688SVladimir Oltean struct switchdev_nested_priv {
3832b0a5688SVladimir Oltean 	bool (*check_cb)(const struct net_device *dev);
3842b0a5688SVladimir Oltean 	bool (*foreign_dev_check_cb)(const struct net_device *dev,
3852b0a5688SVladimir Oltean 				     const struct net_device *foreign_dev);
3862b0a5688SVladimir Oltean 	const struct net_device *dev;
3872b0a5688SVladimir Oltean 	struct net_device *lower_dev;
3882b0a5688SVladimir Oltean };
3892b0a5688SVladimir Oltean 
3902b0a5688SVladimir Oltean static int switchdev_lower_dev_walk(struct net_device *lower_dev,
3912b0a5688SVladimir Oltean 				    struct netdev_nested_priv *priv)
3922b0a5688SVladimir Oltean {
3932b0a5688SVladimir Oltean 	struct switchdev_nested_priv *switchdev_priv = priv->data;
3942b0a5688SVladimir Oltean 	bool (*foreign_dev_check_cb)(const struct net_device *dev,
3952b0a5688SVladimir Oltean 				     const struct net_device *foreign_dev);
3962b0a5688SVladimir Oltean 	bool (*check_cb)(const struct net_device *dev);
3972b0a5688SVladimir Oltean 	const struct net_device *dev;
3982b0a5688SVladimir Oltean 
3992b0a5688SVladimir Oltean 	check_cb = switchdev_priv->check_cb;
4002b0a5688SVladimir Oltean 	foreign_dev_check_cb = switchdev_priv->foreign_dev_check_cb;
4012b0a5688SVladimir Oltean 	dev = switchdev_priv->dev;
4022b0a5688SVladimir Oltean 
4032b0a5688SVladimir Oltean 	if (check_cb(lower_dev) && !foreign_dev_check_cb(lower_dev, dev)) {
4042b0a5688SVladimir Oltean 		switchdev_priv->lower_dev = lower_dev;
4052b0a5688SVladimir Oltean 		return 1;
4062b0a5688SVladimir Oltean 	}
4072b0a5688SVladimir Oltean 
4082b0a5688SVladimir Oltean 	return 0;
4092b0a5688SVladimir Oltean }
4102b0a5688SVladimir Oltean 
4112b0a5688SVladimir Oltean static struct net_device *
4127b465f4cSVladimir Oltean switchdev_lower_dev_find_rcu(struct net_device *dev,
4132b0a5688SVladimir Oltean 			     bool (*check_cb)(const struct net_device *dev),
4142b0a5688SVladimir Oltean 			     bool (*foreign_dev_check_cb)(const struct net_device *dev,
4152b0a5688SVladimir Oltean 							  const struct net_device *foreign_dev))
4162b0a5688SVladimir Oltean {
4172b0a5688SVladimir Oltean 	struct switchdev_nested_priv switchdev_priv = {
4182b0a5688SVladimir Oltean 		.check_cb = check_cb,
4192b0a5688SVladimir Oltean 		.foreign_dev_check_cb = foreign_dev_check_cb,
4202b0a5688SVladimir Oltean 		.dev = dev,
4212b0a5688SVladimir Oltean 		.lower_dev = NULL,
4222b0a5688SVladimir Oltean 	};
4232b0a5688SVladimir Oltean 	struct netdev_nested_priv priv = {
4242b0a5688SVladimir Oltean 		.data = &switchdev_priv,
4252b0a5688SVladimir Oltean 	};
4262b0a5688SVladimir Oltean 
4272b0a5688SVladimir Oltean 	netdev_walk_all_lower_dev_rcu(dev, switchdev_lower_dev_walk, &priv);
4282b0a5688SVladimir Oltean 
4292b0a5688SVladimir Oltean 	return switchdev_priv.lower_dev;
4302b0a5688SVladimir Oltean }
4312b0a5688SVladimir Oltean 
432*c4076cddSVladimir Oltean static struct net_device *
433*c4076cddSVladimir Oltean switchdev_lower_dev_find(struct net_device *dev,
434*c4076cddSVladimir Oltean 			 bool (*check_cb)(const struct net_device *dev),
435*c4076cddSVladimir Oltean 			 bool (*foreign_dev_check_cb)(const struct net_device *dev,
436*c4076cddSVladimir Oltean 						      const struct net_device *foreign_dev))
437*c4076cddSVladimir Oltean {
438*c4076cddSVladimir Oltean 	struct switchdev_nested_priv switchdev_priv = {
439*c4076cddSVladimir Oltean 		.check_cb = check_cb,
440*c4076cddSVladimir Oltean 		.foreign_dev_check_cb = foreign_dev_check_cb,
441*c4076cddSVladimir Oltean 		.dev = dev,
442*c4076cddSVladimir Oltean 		.lower_dev = NULL,
443*c4076cddSVladimir Oltean 	};
444*c4076cddSVladimir Oltean 	struct netdev_nested_priv priv = {
445*c4076cddSVladimir Oltean 		.data = &switchdev_priv,
446*c4076cddSVladimir Oltean 	};
447*c4076cddSVladimir Oltean 
448*c4076cddSVladimir Oltean 	netdev_walk_all_lower_dev(dev, switchdev_lower_dev_walk, &priv);
449*c4076cddSVladimir Oltean 
450*c4076cddSVladimir Oltean 	return switchdev_priv.lower_dev;
451*c4076cddSVladimir Oltean }
452*c4076cddSVladimir Oltean 
453716a30a9SVladimir Oltean static int __switchdev_handle_fdb_event_to_device(struct net_device *dev,
454716a30a9SVladimir Oltean 		struct net_device *orig_dev, unsigned long event,
4558ca07176SVladimir Oltean 		const struct switchdev_notifier_fdb_info *fdb_info,
4568ca07176SVladimir Oltean 		bool (*check_cb)(const struct net_device *dev),
4578ca07176SVladimir Oltean 		bool (*foreign_dev_check_cb)(const struct net_device *dev,
4588ca07176SVladimir Oltean 					     const struct net_device *foreign_dev),
459716a30a9SVladimir Oltean 		int (*mod_cb)(struct net_device *dev, struct net_device *orig_dev,
460716a30a9SVladimir Oltean 			      unsigned long event, const void *ctx,
4618ca07176SVladimir Oltean 			      const struct switchdev_notifier_fdb_info *fdb_info),
462716a30a9SVladimir Oltean 		int (*lag_mod_cb)(struct net_device *dev, struct net_device *orig_dev,
463716a30a9SVladimir Oltean 				  unsigned long event, const void *ctx,
4648ca07176SVladimir Oltean 				  const struct switchdev_notifier_fdb_info *fdb_info))
4658ca07176SVladimir Oltean {
4668ca07176SVladimir Oltean 	const struct switchdev_notifier_info *info = &fdb_info->info;
4672b0a5688SVladimir Oltean 	struct net_device *br, *lower_dev;
4688ca07176SVladimir Oltean 	struct list_head *iter;
4698ca07176SVladimir Oltean 	int err = -EOPNOTSUPP;
4708ca07176SVladimir Oltean 
4712b0a5688SVladimir Oltean 	if (check_cb(dev))
472716a30a9SVladimir Oltean 		return mod_cb(dev, orig_dev, event, info->ctx, fdb_info);
4738ca07176SVladimir Oltean 
4748ca07176SVladimir Oltean 	if (netif_is_lag_master(dev)) {
4757b465f4cSVladimir Oltean 		if (!switchdev_lower_dev_find_rcu(dev, check_cb, foreign_dev_check_cb))
4762b0a5688SVladimir Oltean 			goto maybe_bridged_with_us;
4772b0a5688SVladimir Oltean 
4782b0a5688SVladimir Oltean 		/* This is a LAG interface that we offload */
479716a30a9SVladimir Oltean 		if (!lag_mod_cb)
4808ca07176SVladimir Oltean 			return -EOPNOTSUPP;
4818ca07176SVladimir Oltean 
482716a30a9SVladimir Oltean 		return lag_mod_cb(dev, orig_dev, event, info->ctx, fdb_info);
4838ca07176SVladimir Oltean 	}
4848ca07176SVladimir Oltean 
4858ca07176SVladimir Oltean 	/* Recurse through lower interfaces in case the FDB entry is pointing
4868ca07176SVladimir Oltean 	 * towards a bridge device.
4878ca07176SVladimir Oltean 	 */
4882b0a5688SVladimir Oltean 	if (netif_is_bridge_master(dev)) {
4897b465f4cSVladimir Oltean 		if (!switchdev_lower_dev_find_rcu(dev, check_cb, foreign_dev_check_cb))
4902b0a5688SVladimir Oltean 			return 0;
4912b0a5688SVladimir Oltean 
4922b0a5688SVladimir Oltean 		/* This is a bridge interface that we offload */
4938ca07176SVladimir Oltean 		netdev_for_each_lower_dev(dev, lower_dev, iter) {
4948ca07176SVladimir Oltean 			/* Do not propagate FDB entries across bridges */
4958ca07176SVladimir Oltean 			if (netif_is_bridge_master(lower_dev))
4968ca07176SVladimir Oltean 				continue;
4978ca07176SVladimir Oltean 
4982b0a5688SVladimir Oltean 			/* Bridge ports might be either us, or LAG interfaces
4992b0a5688SVladimir Oltean 			 * that we offload.
5002b0a5688SVladimir Oltean 			 */
5012b0a5688SVladimir Oltean 			if (!check_cb(lower_dev) &&
5027b465f4cSVladimir Oltean 			    !switchdev_lower_dev_find_rcu(lower_dev, check_cb,
5032b0a5688SVladimir Oltean 							  foreign_dev_check_cb))
5042b0a5688SVladimir Oltean 				continue;
5052b0a5688SVladimir Oltean 
506716a30a9SVladimir Oltean 			err = __switchdev_handle_fdb_event_to_device(lower_dev, orig_dev,
507716a30a9SVladimir Oltean 								     event, fdb_info, check_cb,
5088ca07176SVladimir Oltean 								     foreign_dev_check_cb,
509716a30a9SVladimir Oltean 								     mod_cb, lag_mod_cb);
5108ca07176SVladimir Oltean 			if (err && err != -EOPNOTSUPP)
5118ca07176SVladimir Oltean 				return err;
5128ca07176SVladimir Oltean 		}
5138ca07176SVladimir Oltean 
5142b0a5688SVladimir Oltean 		return 0;
5152b0a5688SVladimir Oltean 	}
5162b0a5688SVladimir Oltean 
5172b0a5688SVladimir Oltean maybe_bridged_with_us:
5182b0a5688SVladimir Oltean 	/* Event is neither on a bridge nor a LAG. Check whether it is on an
5192b0a5688SVladimir Oltean 	 * interface that is in a bridge with us.
5202b0a5688SVladimir Oltean 	 */
5212b0a5688SVladimir Oltean 	br = netdev_master_upper_dev_get_rcu(dev);
5222b0a5688SVladimir Oltean 	if (!br || !netif_is_bridge_master(br))
5232b0a5688SVladimir Oltean 		return 0;
5242b0a5688SVladimir Oltean 
5257b465f4cSVladimir Oltean 	if (!switchdev_lower_dev_find_rcu(br, check_cb, foreign_dev_check_cb))
5262b0a5688SVladimir Oltean 		return 0;
5272b0a5688SVladimir Oltean 
528716a30a9SVladimir Oltean 	return __switchdev_handle_fdb_event_to_device(br, orig_dev, event, fdb_info,
5292b0a5688SVladimir Oltean 						      check_cb, foreign_dev_check_cb,
530716a30a9SVladimir Oltean 						      mod_cb, lag_mod_cb);
5318ca07176SVladimir Oltean }
5328ca07176SVladimir Oltean 
533716a30a9SVladimir Oltean int switchdev_handle_fdb_event_to_device(struct net_device *dev, unsigned long event,
5348ca07176SVladimir Oltean 		const struct switchdev_notifier_fdb_info *fdb_info,
5358ca07176SVladimir Oltean 		bool (*check_cb)(const struct net_device *dev),
5368ca07176SVladimir Oltean 		bool (*foreign_dev_check_cb)(const struct net_device *dev,
5378ca07176SVladimir Oltean 					     const struct net_device *foreign_dev),
538716a30a9SVladimir Oltean 		int (*mod_cb)(struct net_device *dev, struct net_device *orig_dev,
539716a30a9SVladimir Oltean 			      unsigned long event, const void *ctx,
5408ca07176SVladimir Oltean 			      const struct switchdev_notifier_fdb_info *fdb_info),
541716a30a9SVladimir Oltean 		int (*lag_mod_cb)(struct net_device *dev, struct net_device *orig_dev,
542716a30a9SVladimir Oltean 				  unsigned long event, const void *ctx,
5438ca07176SVladimir Oltean 				  const struct switchdev_notifier_fdb_info *fdb_info))
5448ca07176SVladimir Oltean {
5458ca07176SVladimir Oltean 	int err;
5468ca07176SVladimir Oltean 
547716a30a9SVladimir Oltean 	err = __switchdev_handle_fdb_event_to_device(dev, dev, event, fdb_info,
548716a30a9SVladimir Oltean 						     check_cb, foreign_dev_check_cb,
549716a30a9SVladimir Oltean 						     mod_cb, lag_mod_cb);
5508ca07176SVladimir Oltean 	if (err == -EOPNOTSUPP)
5518ca07176SVladimir Oltean 		err = 0;
5528ca07176SVladimir Oltean 
5538ca07176SVladimir Oltean 	return err;
5548ca07176SVladimir Oltean }
555716a30a9SVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_handle_fdb_event_to_device);
5568ca07176SVladimir Oltean 
557f30f0601SPetr Machata static int __switchdev_handle_port_obj_add(struct net_device *dev,
558f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
559f30f0601SPetr Machata 			bool (*check_cb)(const struct net_device *dev),
560*c4076cddSVladimir Oltean 			bool (*foreign_dev_check_cb)(const struct net_device *dev,
561*c4076cddSVladimir Oltean 						     const struct net_device *foreign_dev),
56269bfac96SVladimir Oltean 			int (*add_cb)(struct net_device *dev, const void *ctx,
563f30f0601SPetr Machata 				      const struct switchdev_obj *obj,
56469213513SPetr Machata 				      struct netlink_ext_ack *extack))
565f30f0601SPetr Machata {
56669bfac96SVladimir Oltean 	struct switchdev_notifier_info *info = &port_obj_info->info;
567*c4076cddSVladimir Oltean 	struct net_device *br, *lower_dev;
56869213513SPetr Machata 	struct netlink_ext_ack *extack;
569f30f0601SPetr Machata 	struct list_head *iter;
570f30f0601SPetr Machata 	int err = -EOPNOTSUPP;
571f30f0601SPetr Machata 
57269bfac96SVladimir Oltean 	extack = switchdev_notifier_info_to_extack(info);
57369213513SPetr Machata 
574f30f0601SPetr Machata 	if (check_cb(dev)) {
57569bfac96SVladimir Oltean 		err = add_cb(dev, info->ctx, port_obj_info->obj, extack);
57620776b46SRasmus Villemoes 		if (err != -EOPNOTSUPP)
577f30f0601SPetr Machata 			port_obj_info->handled = true;
57820776b46SRasmus Villemoes 		return err;
579f30f0601SPetr Machata 	}
580f30f0601SPetr Machata 
581f30f0601SPetr Machata 	/* Switch ports might be stacked under e.g. a LAG. Ignore the
582f30f0601SPetr Machata 	 * unsupported devices, another driver might be able to handle them. But
583f30f0601SPetr Machata 	 * propagate to the callers any hard errors.
584f30f0601SPetr Machata 	 *
585f30f0601SPetr Machata 	 * If the driver does its own bookkeeping of stacked ports, it's not
586f30f0601SPetr Machata 	 * necessary to go through this helper.
587f30f0601SPetr Machata 	 */
588f30f0601SPetr Machata 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
58907c6f980SRussell King 		if (netif_is_bridge_master(lower_dev))
59007c6f980SRussell King 			continue;
59107c6f980SRussell King 
592*c4076cddSVladimir Oltean 		/* When searching for switchdev interfaces that are neighbors
593*c4076cddSVladimir Oltean 		 * of foreign ones, and @dev is a bridge, do not recurse on the
594*c4076cddSVladimir Oltean 		 * foreign interface again, it was already visited.
595*c4076cddSVladimir Oltean 		 */
596*c4076cddSVladimir Oltean 		if (foreign_dev_check_cb && !check_cb(lower_dev) &&
597*c4076cddSVladimir Oltean 		    !switchdev_lower_dev_find(lower_dev, check_cb, foreign_dev_check_cb))
598*c4076cddSVladimir Oltean 			continue;
599*c4076cddSVladimir Oltean 
600f30f0601SPetr Machata 		err = __switchdev_handle_port_obj_add(lower_dev, port_obj_info,
601*c4076cddSVladimir Oltean 						      check_cb, foreign_dev_check_cb,
602*c4076cddSVladimir Oltean 						      add_cb);
603f30f0601SPetr Machata 		if (err && err != -EOPNOTSUPP)
604f30f0601SPetr Machata 			return err;
605f30f0601SPetr Machata 	}
606f30f0601SPetr Machata 
607*c4076cddSVladimir Oltean 	/* Event is neither on a bridge nor a LAG. Check whether it is on an
608*c4076cddSVladimir Oltean 	 * interface that is in a bridge with us.
609*c4076cddSVladimir Oltean 	 */
610*c4076cddSVladimir Oltean 	if (!foreign_dev_check_cb)
611f30f0601SPetr Machata 		return err;
612*c4076cddSVladimir Oltean 
613*c4076cddSVladimir Oltean 	br = netdev_master_upper_dev_get(dev);
614*c4076cddSVladimir Oltean 	if (!br || !netif_is_bridge_master(br))
615*c4076cddSVladimir Oltean 		return err;
616*c4076cddSVladimir Oltean 
617*c4076cddSVladimir Oltean 	if (!switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb))
618*c4076cddSVladimir Oltean 		return err;
619*c4076cddSVladimir Oltean 
620*c4076cddSVladimir Oltean 	return __switchdev_handle_port_obj_add(br, port_obj_info, check_cb,
621*c4076cddSVladimir Oltean 					       foreign_dev_check_cb, add_cb);
622f30f0601SPetr Machata }
623f30f0601SPetr Machata 
624*c4076cddSVladimir Oltean /* Pass through a port object addition, if @dev passes @check_cb, or replicate
625*c4076cddSVladimir Oltean  * it towards all lower interfaces of @dev that pass @check_cb, if @dev is a
626*c4076cddSVladimir Oltean  * bridge or a LAG.
627*c4076cddSVladimir Oltean  */
628f30f0601SPetr Machata int switchdev_handle_port_obj_add(struct net_device *dev,
629f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
630f30f0601SPetr Machata 			bool (*check_cb)(const struct net_device *dev),
63169bfac96SVladimir Oltean 			int (*add_cb)(struct net_device *dev, const void *ctx,
632f30f0601SPetr Machata 				      const struct switchdev_obj *obj,
63369213513SPetr Machata 				      struct netlink_ext_ack *extack))
634f30f0601SPetr Machata {
635f30f0601SPetr Machata 	int err;
636f30f0601SPetr Machata 
637f30f0601SPetr Machata 	err = __switchdev_handle_port_obj_add(dev, port_obj_info, check_cb,
638*c4076cddSVladimir Oltean 					      NULL, add_cb);
639f30f0601SPetr Machata 	if (err == -EOPNOTSUPP)
640f30f0601SPetr Machata 		err = 0;
641f30f0601SPetr Machata 	return err;
642f30f0601SPetr Machata }
643f30f0601SPetr Machata EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add);
644f30f0601SPetr Machata 
645*c4076cddSVladimir Oltean /* Same as switchdev_handle_port_obj_add(), except if object is notified on a
646*c4076cddSVladimir Oltean  * @dev that passes @foreign_dev_check_cb, it is replicated towards all devices
647*c4076cddSVladimir Oltean  * that pass @check_cb and are in the same bridge as @dev.
648*c4076cddSVladimir Oltean  */
649*c4076cddSVladimir Oltean int switchdev_handle_port_obj_add_foreign(struct net_device *dev,
650*c4076cddSVladimir Oltean 			struct switchdev_notifier_port_obj_info *port_obj_info,
651*c4076cddSVladimir Oltean 			bool (*check_cb)(const struct net_device *dev),
652*c4076cddSVladimir Oltean 			bool (*foreign_dev_check_cb)(const struct net_device *dev,
653*c4076cddSVladimir Oltean 						     const struct net_device *foreign_dev),
654*c4076cddSVladimir Oltean 			int (*add_cb)(struct net_device *dev, const void *ctx,
655*c4076cddSVladimir Oltean 				      const struct switchdev_obj *obj,
656*c4076cddSVladimir Oltean 				      struct netlink_ext_ack *extack))
657*c4076cddSVladimir Oltean {
658*c4076cddSVladimir Oltean 	int err;
659*c4076cddSVladimir Oltean 
660*c4076cddSVladimir Oltean 	err = __switchdev_handle_port_obj_add(dev, port_obj_info, check_cb,
661*c4076cddSVladimir Oltean 					      foreign_dev_check_cb, add_cb);
662*c4076cddSVladimir Oltean 	if (err == -EOPNOTSUPP)
663*c4076cddSVladimir Oltean 		err = 0;
664*c4076cddSVladimir Oltean 	return err;
665*c4076cddSVladimir Oltean }
666*c4076cddSVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add_foreign);
667*c4076cddSVladimir Oltean 
668f30f0601SPetr Machata static int __switchdev_handle_port_obj_del(struct net_device *dev,
669f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
670f30f0601SPetr Machata 			bool (*check_cb)(const struct net_device *dev),
671*c4076cddSVladimir Oltean 			bool (*foreign_dev_check_cb)(const struct net_device *dev,
672*c4076cddSVladimir Oltean 						     const struct net_device *foreign_dev),
67369bfac96SVladimir Oltean 			int (*del_cb)(struct net_device *dev, const void *ctx,
674f30f0601SPetr Machata 				      const struct switchdev_obj *obj))
675f30f0601SPetr Machata {
67669bfac96SVladimir Oltean 	struct switchdev_notifier_info *info = &port_obj_info->info;
677*c4076cddSVladimir Oltean 	struct net_device *br, *lower_dev;
678f30f0601SPetr Machata 	struct list_head *iter;
679f30f0601SPetr Machata 	int err = -EOPNOTSUPP;
680f30f0601SPetr Machata 
681f30f0601SPetr Machata 	if (check_cb(dev)) {
68269bfac96SVladimir Oltean 		err = del_cb(dev, info->ctx, port_obj_info->obj);
68320776b46SRasmus Villemoes 		if (err != -EOPNOTSUPP)
684f30f0601SPetr Machata 			port_obj_info->handled = true;
68520776b46SRasmus Villemoes 		return err;
686f30f0601SPetr Machata 	}
687f30f0601SPetr Machata 
688f30f0601SPetr Machata 	/* Switch ports might be stacked under e.g. a LAG. Ignore the
689f30f0601SPetr Machata 	 * unsupported devices, another driver might be able to handle them. But
690f30f0601SPetr Machata 	 * propagate to the callers any hard errors.
691f30f0601SPetr Machata 	 *
692f30f0601SPetr Machata 	 * If the driver does its own bookkeeping of stacked ports, it's not
693f30f0601SPetr Machata 	 * necessary to go through this helper.
694f30f0601SPetr Machata 	 */
695f30f0601SPetr Machata 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
69607c6f980SRussell King 		if (netif_is_bridge_master(lower_dev))
69707c6f980SRussell King 			continue;
69807c6f980SRussell King 
699*c4076cddSVladimir Oltean 		/* When searching for switchdev interfaces that are neighbors
700*c4076cddSVladimir Oltean 		 * of foreign ones, and @dev is a bridge, do not recurse on the
701*c4076cddSVladimir Oltean 		 * foreign interface again, it was already visited.
702*c4076cddSVladimir Oltean 		 */
703*c4076cddSVladimir Oltean 		if (foreign_dev_check_cb && !check_cb(lower_dev) &&
704*c4076cddSVladimir Oltean 		    !switchdev_lower_dev_find(lower_dev, check_cb, foreign_dev_check_cb))
705*c4076cddSVladimir Oltean 			continue;
706*c4076cddSVladimir Oltean 
707f30f0601SPetr Machata 		err = __switchdev_handle_port_obj_del(lower_dev, port_obj_info,
708*c4076cddSVladimir Oltean 						      check_cb, foreign_dev_check_cb,
709*c4076cddSVladimir Oltean 						      del_cb);
710f30f0601SPetr Machata 		if (err && err != -EOPNOTSUPP)
711f30f0601SPetr Machata 			return err;
712f30f0601SPetr Machata 	}
713f30f0601SPetr Machata 
714*c4076cddSVladimir Oltean 	/* Event is neither on a bridge nor a LAG. Check whether it is on an
715*c4076cddSVladimir Oltean 	 * interface that is in a bridge with us.
716*c4076cddSVladimir Oltean 	 */
717*c4076cddSVladimir Oltean 	if (!foreign_dev_check_cb)
718f30f0601SPetr Machata 		return err;
719*c4076cddSVladimir Oltean 
720*c4076cddSVladimir Oltean 	br = netdev_master_upper_dev_get(dev);
721*c4076cddSVladimir Oltean 	if (!br || !netif_is_bridge_master(br))
722*c4076cddSVladimir Oltean 		return err;
723*c4076cddSVladimir Oltean 
724*c4076cddSVladimir Oltean 	if (!switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb))
725*c4076cddSVladimir Oltean 		return err;
726*c4076cddSVladimir Oltean 
727*c4076cddSVladimir Oltean 	return __switchdev_handle_port_obj_del(br, port_obj_info, check_cb,
728*c4076cddSVladimir Oltean 					       foreign_dev_check_cb, del_cb);
729f30f0601SPetr Machata }
730f30f0601SPetr Machata 
731*c4076cddSVladimir Oltean /* Pass through a port object deletion, if @dev passes @check_cb, or replicate
732*c4076cddSVladimir Oltean  * it towards all lower interfaces of @dev that pass @check_cb, if @dev is a
733*c4076cddSVladimir Oltean  * bridge or a LAG.
734*c4076cddSVladimir Oltean  */
735f30f0601SPetr Machata int switchdev_handle_port_obj_del(struct net_device *dev,
736f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
737f30f0601SPetr Machata 			bool (*check_cb)(const struct net_device *dev),
73869bfac96SVladimir Oltean 			int (*del_cb)(struct net_device *dev, const void *ctx,
739f30f0601SPetr Machata 				      const struct switchdev_obj *obj))
740f30f0601SPetr Machata {
741f30f0601SPetr Machata 	int err;
742f30f0601SPetr Machata 
743f30f0601SPetr Machata 	err = __switchdev_handle_port_obj_del(dev, port_obj_info, check_cb,
744*c4076cddSVladimir Oltean 					      NULL, del_cb);
745f30f0601SPetr Machata 	if (err == -EOPNOTSUPP)
746f30f0601SPetr Machata 		err = 0;
747f30f0601SPetr Machata 	return err;
748f30f0601SPetr Machata }
749f30f0601SPetr Machata EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del);
7501cb33af1SFlorian Fainelli 
751*c4076cddSVladimir Oltean /* Same as switchdev_handle_port_obj_del(), except if object is notified on a
752*c4076cddSVladimir Oltean  * @dev that passes @foreign_dev_check_cb, it is replicated towards all devices
753*c4076cddSVladimir Oltean  * that pass @check_cb and are in the same bridge as @dev.
754*c4076cddSVladimir Oltean  */
755*c4076cddSVladimir Oltean int switchdev_handle_port_obj_del_foreign(struct net_device *dev,
756*c4076cddSVladimir Oltean 			struct switchdev_notifier_port_obj_info *port_obj_info,
757*c4076cddSVladimir Oltean 			bool (*check_cb)(const struct net_device *dev),
758*c4076cddSVladimir Oltean 			bool (*foreign_dev_check_cb)(const struct net_device *dev,
759*c4076cddSVladimir Oltean 						     const struct net_device *foreign_dev),
760*c4076cddSVladimir Oltean 			int (*del_cb)(struct net_device *dev, const void *ctx,
761*c4076cddSVladimir Oltean 				      const struct switchdev_obj *obj))
762*c4076cddSVladimir Oltean {
763*c4076cddSVladimir Oltean 	int err;
764*c4076cddSVladimir Oltean 
765*c4076cddSVladimir Oltean 	err = __switchdev_handle_port_obj_del(dev, port_obj_info, check_cb,
766*c4076cddSVladimir Oltean 					      foreign_dev_check_cb, del_cb);
767*c4076cddSVladimir Oltean 	if (err == -EOPNOTSUPP)
768*c4076cddSVladimir Oltean 		err = 0;
769*c4076cddSVladimir Oltean 	return err;
770*c4076cddSVladimir Oltean }
771*c4076cddSVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del_foreign);
772*c4076cddSVladimir Oltean 
7731cb33af1SFlorian Fainelli static int __switchdev_handle_port_attr_set(struct net_device *dev,
7741cb33af1SFlorian Fainelli 			struct switchdev_notifier_port_attr_info *port_attr_info,
7751cb33af1SFlorian Fainelli 			bool (*check_cb)(const struct net_device *dev),
77669bfac96SVladimir Oltean 			int (*set_cb)(struct net_device *dev, const void *ctx,
7774c08c586SVladimir Oltean 				      const struct switchdev_attr *attr,
7784c08c586SVladimir Oltean 				      struct netlink_ext_ack *extack))
7791cb33af1SFlorian Fainelli {
78069bfac96SVladimir Oltean 	struct switchdev_notifier_info *info = &port_attr_info->info;
7814c08c586SVladimir Oltean 	struct netlink_ext_ack *extack;
7821cb33af1SFlorian Fainelli 	struct net_device *lower_dev;
7831cb33af1SFlorian Fainelli 	struct list_head *iter;
7841cb33af1SFlorian Fainelli 	int err = -EOPNOTSUPP;
7851cb33af1SFlorian Fainelli 
78669bfac96SVladimir Oltean 	extack = switchdev_notifier_info_to_extack(info);
7874c08c586SVladimir Oltean 
7881cb33af1SFlorian Fainelli 	if (check_cb(dev)) {
78969bfac96SVladimir Oltean 		err = set_cb(dev, info->ctx, port_attr_info->attr, extack);
79020776b46SRasmus Villemoes 		if (err != -EOPNOTSUPP)
7911cb33af1SFlorian Fainelli 			port_attr_info->handled = true;
79220776b46SRasmus Villemoes 		return err;
7931cb33af1SFlorian Fainelli 	}
7941cb33af1SFlorian Fainelli 
7951cb33af1SFlorian Fainelli 	/* Switch ports might be stacked under e.g. a LAG. Ignore the
7961cb33af1SFlorian Fainelli 	 * unsupported devices, another driver might be able to handle them. But
7971cb33af1SFlorian Fainelli 	 * propagate to the callers any hard errors.
7981cb33af1SFlorian Fainelli 	 *
7991cb33af1SFlorian Fainelli 	 * If the driver does its own bookkeeping of stacked ports, it's not
8001cb33af1SFlorian Fainelli 	 * necessary to go through this helper.
8011cb33af1SFlorian Fainelli 	 */
8021cb33af1SFlorian Fainelli 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
80307c6f980SRussell King 		if (netif_is_bridge_master(lower_dev))
80407c6f980SRussell King 			continue;
80507c6f980SRussell King 
8061cb33af1SFlorian Fainelli 		err = __switchdev_handle_port_attr_set(lower_dev, port_attr_info,
8071cb33af1SFlorian Fainelli 						       check_cb, set_cb);
8081cb33af1SFlorian Fainelli 		if (err && err != -EOPNOTSUPP)
8091cb33af1SFlorian Fainelli 			return err;
8101cb33af1SFlorian Fainelli 	}
8111cb33af1SFlorian Fainelli 
8121cb33af1SFlorian Fainelli 	return err;
8131cb33af1SFlorian Fainelli }
8141cb33af1SFlorian Fainelli 
8151cb33af1SFlorian Fainelli int switchdev_handle_port_attr_set(struct net_device *dev,
8161cb33af1SFlorian Fainelli 			struct switchdev_notifier_port_attr_info *port_attr_info,
8171cb33af1SFlorian Fainelli 			bool (*check_cb)(const struct net_device *dev),
81869bfac96SVladimir Oltean 			int (*set_cb)(struct net_device *dev, const void *ctx,
8194c08c586SVladimir Oltean 				      const struct switchdev_attr *attr,
8204c08c586SVladimir Oltean 				      struct netlink_ext_ack *extack))
8211cb33af1SFlorian Fainelli {
8221cb33af1SFlorian Fainelli 	int err;
8231cb33af1SFlorian Fainelli 
8241cb33af1SFlorian Fainelli 	err = __switchdev_handle_port_attr_set(dev, port_attr_info, check_cb,
8251cb33af1SFlorian Fainelli 					       set_cb);
8261cb33af1SFlorian Fainelli 	if (err == -EOPNOTSUPP)
8271cb33af1SFlorian Fainelli 		err = 0;
8281cb33af1SFlorian Fainelli 	return err;
8291cb33af1SFlorian Fainelli }
8301cb33af1SFlorian Fainelli EXPORT_SYMBOL_GPL(switchdev_handle_port_attr_set);
831957e2235SVladimir Oltean 
832957e2235SVladimir Oltean int switchdev_bridge_port_offload(struct net_device *brport_dev,
833957e2235SVladimir Oltean 				  struct net_device *dev, const void *ctx,
834957e2235SVladimir Oltean 				  struct notifier_block *atomic_nb,
835957e2235SVladimir Oltean 				  struct notifier_block *blocking_nb,
836957e2235SVladimir Oltean 				  bool tx_fwd_offload,
837957e2235SVladimir Oltean 				  struct netlink_ext_ack *extack)
838957e2235SVladimir Oltean {
839957e2235SVladimir Oltean 	struct switchdev_notifier_brport_info brport_info = {
840957e2235SVladimir Oltean 		.brport = {
841957e2235SVladimir Oltean 			.dev = dev,
842957e2235SVladimir Oltean 			.ctx = ctx,
843957e2235SVladimir Oltean 			.atomic_nb = atomic_nb,
844957e2235SVladimir Oltean 			.blocking_nb = blocking_nb,
845957e2235SVladimir Oltean 			.tx_fwd_offload = tx_fwd_offload,
846957e2235SVladimir Oltean 		},
847957e2235SVladimir Oltean 	};
848957e2235SVladimir Oltean 	int err;
849957e2235SVladimir Oltean 
850957e2235SVladimir Oltean 	ASSERT_RTNL();
851957e2235SVladimir Oltean 
852957e2235SVladimir Oltean 	err = call_switchdev_blocking_notifiers(SWITCHDEV_BRPORT_OFFLOADED,
853957e2235SVladimir Oltean 						brport_dev, &brport_info.info,
854957e2235SVladimir Oltean 						extack);
855957e2235SVladimir Oltean 	return notifier_to_errno(err);
856957e2235SVladimir Oltean }
857957e2235SVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_bridge_port_offload);
858957e2235SVladimir Oltean 
859957e2235SVladimir Oltean void switchdev_bridge_port_unoffload(struct net_device *brport_dev,
860957e2235SVladimir Oltean 				     const void *ctx,
861957e2235SVladimir Oltean 				     struct notifier_block *atomic_nb,
862957e2235SVladimir Oltean 				     struct notifier_block *blocking_nb)
863957e2235SVladimir Oltean {
864957e2235SVladimir Oltean 	struct switchdev_notifier_brport_info brport_info = {
865957e2235SVladimir Oltean 		.brport = {
866957e2235SVladimir Oltean 			.ctx = ctx,
867957e2235SVladimir Oltean 			.atomic_nb = atomic_nb,
868957e2235SVladimir Oltean 			.blocking_nb = blocking_nb,
869957e2235SVladimir Oltean 		},
870957e2235SVladimir Oltean 	};
871957e2235SVladimir Oltean 
872957e2235SVladimir Oltean 	ASSERT_RTNL();
873957e2235SVladimir Oltean 
874957e2235SVladimir Oltean 	call_switchdev_blocking_notifiers(SWITCHDEV_BRPORT_UNOFFLOADED,
875957e2235SVladimir Oltean 					  brport_dev, &brport_info.info,
876957e2235SVladimir Oltean 					  NULL);
877957e2235SVladimir Oltean }
878957e2235SVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_bridge_port_unoffload);
879