xref: /linux-6.15/net/switchdev/switchdev.c (revision dc489f86)
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 
22*dc489f86STobias Waldekranz static bool switchdev_obj_eq(const struct switchdev_obj *a,
23*dc489f86STobias Waldekranz 			     const struct switchdev_obj *b)
24*dc489f86STobias Waldekranz {
25*dc489f86STobias Waldekranz 	const struct switchdev_obj_port_vlan *va, *vb;
26*dc489f86STobias Waldekranz 	const struct switchdev_obj_port_mdb *ma, *mb;
27*dc489f86STobias Waldekranz 
28*dc489f86STobias Waldekranz 	if (a->id != b->id || a->orig_dev != b->orig_dev)
29*dc489f86STobias Waldekranz 		return false;
30*dc489f86STobias Waldekranz 
31*dc489f86STobias Waldekranz 	switch (a->id) {
32*dc489f86STobias Waldekranz 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
33*dc489f86STobias Waldekranz 		va = SWITCHDEV_OBJ_PORT_VLAN(a);
34*dc489f86STobias Waldekranz 		vb = SWITCHDEV_OBJ_PORT_VLAN(b);
35*dc489f86STobias Waldekranz 		return va->flags == vb->flags &&
36*dc489f86STobias Waldekranz 			va->vid == vb->vid &&
37*dc489f86STobias Waldekranz 			va->changed == vb->changed;
38*dc489f86STobias Waldekranz 	case SWITCHDEV_OBJ_ID_PORT_MDB:
39*dc489f86STobias Waldekranz 	case SWITCHDEV_OBJ_ID_HOST_MDB:
40*dc489f86STobias Waldekranz 		ma = SWITCHDEV_OBJ_PORT_MDB(a);
41*dc489f86STobias Waldekranz 		mb = SWITCHDEV_OBJ_PORT_MDB(b);
42*dc489f86STobias Waldekranz 		return ma->vid == mb->vid &&
43*dc489f86STobias Waldekranz 			ether_addr_equal(ma->addr, mb->addr);
44*dc489f86STobias Waldekranz 	default:
45*dc489f86STobias Waldekranz 		break;
46*dc489f86STobias Waldekranz 	}
47*dc489f86STobias Waldekranz 
48*dc489f86STobias Waldekranz 	BUG();
49*dc489f86STobias Waldekranz }
50*dc489f86STobias Waldekranz 
51793f4014SJiri Pirko static LIST_HEAD(deferred);
52793f4014SJiri Pirko static DEFINE_SPINLOCK(deferred_lock);
53793f4014SJiri Pirko 
54793f4014SJiri Pirko typedef void switchdev_deferred_func_t(struct net_device *dev,
55793f4014SJiri Pirko 				       const void *data);
56793f4014SJiri Pirko 
57793f4014SJiri Pirko struct switchdev_deferred_item {
58793f4014SJiri Pirko 	struct list_head list;
59793f4014SJiri Pirko 	struct net_device *dev;
604fc003feSEric Dumazet 	netdevice_tracker dev_tracker;
61793f4014SJiri Pirko 	switchdev_deferred_func_t *func;
62fbfc8502SGustavo A. R. Silva 	unsigned long data[];
63793f4014SJiri Pirko };
64793f4014SJiri Pirko 
65793f4014SJiri Pirko static struct switchdev_deferred_item *switchdev_deferred_dequeue(void)
66793f4014SJiri Pirko {
67793f4014SJiri Pirko 	struct switchdev_deferred_item *dfitem;
68793f4014SJiri Pirko 
69793f4014SJiri Pirko 	spin_lock_bh(&deferred_lock);
70793f4014SJiri Pirko 	if (list_empty(&deferred)) {
71793f4014SJiri Pirko 		dfitem = NULL;
72793f4014SJiri Pirko 		goto unlock;
73793f4014SJiri Pirko 	}
74793f4014SJiri Pirko 	dfitem = list_first_entry(&deferred,
75793f4014SJiri Pirko 				  struct switchdev_deferred_item, list);
76793f4014SJiri Pirko 	list_del(&dfitem->list);
77793f4014SJiri Pirko unlock:
78793f4014SJiri Pirko 	spin_unlock_bh(&deferred_lock);
79793f4014SJiri Pirko 	return dfitem;
80793f4014SJiri Pirko }
81793f4014SJiri Pirko 
82793f4014SJiri Pirko /**
83793f4014SJiri Pirko  *	switchdev_deferred_process - Process ops in deferred queue
84793f4014SJiri Pirko  *
85793f4014SJiri Pirko  *	Called to flush the ops currently queued in deferred ops queue.
86793f4014SJiri Pirko  *	rtnl_lock must be held.
87793f4014SJiri Pirko  */
88793f4014SJiri Pirko void switchdev_deferred_process(void)
89793f4014SJiri Pirko {
90793f4014SJiri Pirko 	struct switchdev_deferred_item *dfitem;
91793f4014SJiri Pirko 
92793f4014SJiri Pirko 	ASSERT_RTNL();
93793f4014SJiri Pirko 
94793f4014SJiri Pirko 	while ((dfitem = switchdev_deferred_dequeue())) {
95793f4014SJiri Pirko 		dfitem->func(dfitem->dev, dfitem->data);
96d62607c3SJakub Kicinski 		netdev_put(dfitem->dev, &dfitem->dev_tracker);
97793f4014SJiri Pirko 		kfree(dfitem);
98793f4014SJiri Pirko 	}
99793f4014SJiri Pirko }
100793f4014SJiri Pirko EXPORT_SYMBOL_GPL(switchdev_deferred_process);
101793f4014SJiri Pirko 
102793f4014SJiri Pirko static void switchdev_deferred_process_work(struct work_struct *work)
103793f4014SJiri Pirko {
104793f4014SJiri Pirko 	rtnl_lock();
105793f4014SJiri Pirko 	switchdev_deferred_process();
106793f4014SJiri Pirko 	rtnl_unlock();
107793f4014SJiri Pirko }
108793f4014SJiri Pirko 
109793f4014SJiri Pirko static DECLARE_WORK(deferred_process_work, switchdev_deferred_process_work);
110793f4014SJiri Pirko 
111793f4014SJiri Pirko static int switchdev_deferred_enqueue(struct net_device *dev,
112793f4014SJiri Pirko 				      const void *data, size_t data_len,
113793f4014SJiri Pirko 				      switchdev_deferred_func_t *func)
114793f4014SJiri Pirko {
115793f4014SJiri Pirko 	struct switchdev_deferred_item *dfitem;
116793f4014SJiri Pirko 
117d8c28581SMinghao Chi (CGEL ZTE) 	dfitem = kmalloc(struct_size(dfitem, data, data_len), GFP_ATOMIC);
118793f4014SJiri Pirko 	if (!dfitem)
119793f4014SJiri Pirko 		return -ENOMEM;
120793f4014SJiri Pirko 	dfitem->dev = dev;
121793f4014SJiri Pirko 	dfitem->func = func;
122793f4014SJiri Pirko 	memcpy(dfitem->data, data, data_len);
123d62607c3SJakub Kicinski 	netdev_hold(dev, &dfitem->dev_tracker, GFP_ATOMIC);
124793f4014SJiri Pirko 	spin_lock_bh(&deferred_lock);
125793f4014SJiri Pirko 	list_add_tail(&dfitem->list, &deferred);
126793f4014SJiri Pirko 	spin_unlock_bh(&deferred_lock);
127793f4014SJiri Pirko 	schedule_work(&deferred_process_work);
128793f4014SJiri Pirko 	return 0;
129793f4014SJiri Pirko }
130793f4014SJiri Pirko 
131d45224d6SFlorian Fainelli static int switchdev_port_attr_notify(enum switchdev_notifier_type nt,
132d45224d6SFlorian Fainelli 				      struct net_device *dev,
133dcbdf135SVladimir Oltean 				      const struct switchdev_attr *attr,
134dcbdf135SVladimir Oltean 				      struct netlink_ext_ack *extack)
1353094333dSScott Feldman {
136d45224d6SFlorian Fainelli 	int err;
137d45224d6SFlorian Fainelli 	int rc;
1383094333dSScott Feldman 
139d45224d6SFlorian Fainelli 	struct switchdev_notifier_port_attr_info attr_info = {
140d45224d6SFlorian Fainelli 		.attr = attr,
141d45224d6SFlorian Fainelli 		.handled = false,
142d45224d6SFlorian Fainelli 	};
1433094333dSScott Feldman 
144d45224d6SFlorian Fainelli 	rc = call_switchdev_blocking_notifiers(nt, dev,
145dcbdf135SVladimir Oltean 					       &attr_info.info, extack);
146d45224d6SFlorian Fainelli 	err = notifier_to_errno(rc);
147d45224d6SFlorian Fainelli 	if (err) {
148d45224d6SFlorian Fainelli 		WARN_ON(!attr_info.handled);
1493094333dSScott Feldman 		return err;
1503094333dSScott Feldman 	}
1513094333dSScott Feldman 
152d45224d6SFlorian Fainelli 	if (!attr_info.handled)
153d45224d6SFlorian Fainelli 		return -EOPNOTSUPP;
154d45224d6SFlorian Fainelli 
155d45224d6SFlorian Fainelli 	return 0;
156d45224d6SFlorian Fainelli }
157d45224d6SFlorian Fainelli 
1580bc05d58SJiri Pirko static int switchdev_port_attr_set_now(struct net_device *dev,
159dcbdf135SVladimir Oltean 				       const struct switchdev_attr *attr,
160dcbdf135SVladimir Oltean 				       struct netlink_ext_ack *extack)
1613094333dSScott Feldman {
162dcbdf135SVladimir Oltean 	return switchdev_port_attr_notify(SWITCHDEV_PORT_ATTR_SET, dev, attr,
163dcbdf135SVladimir Oltean 					  extack);
1643094333dSScott Feldman }
1650bc05d58SJiri Pirko 
1660bc05d58SJiri Pirko static void switchdev_port_attr_set_deferred(struct net_device *dev,
1670bc05d58SJiri Pirko 					     const void *data)
1680bc05d58SJiri Pirko {
1690bc05d58SJiri Pirko 	const struct switchdev_attr *attr = data;
1700bc05d58SJiri Pirko 	int err;
1710bc05d58SJiri Pirko 
172dcbdf135SVladimir Oltean 	err = switchdev_port_attr_set_now(dev, attr, NULL);
1730bc05d58SJiri Pirko 	if (err && err != -EOPNOTSUPP)
1740bc05d58SJiri Pirko 		netdev_err(dev, "failed (err=%d) to set attribute (id=%d)\n",
1750bc05d58SJiri Pirko 			   err, attr->id);
1767ceb2afbSElad Raz 	if (attr->complete)
1777ceb2afbSElad Raz 		attr->complete(dev, err, attr->complete_priv);
1780bc05d58SJiri Pirko }
1790bc05d58SJiri Pirko 
1800bc05d58SJiri Pirko static int switchdev_port_attr_set_defer(struct net_device *dev,
1810bc05d58SJiri Pirko 					 const struct switchdev_attr *attr)
1820bc05d58SJiri Pirko {
1830bc05d58SJiri Pirko 	return switchdev_deferred_enqueue(dev, attr, sizeof(*attr),
1840bc05d58SJiri Pirko 					  switchdev_port_attr_set_deferred);
1850bc05d58SJiri Pirko }
1860bc05d58SJiri Pirko 
1870bc05d58SJiri Pirko /**
1880bc05d58SJiri Pirko  *	switchdev_port_attr_set - Set port attribute
1890bc05d58SJiri Pirko  *
1900bc05d58SJiri Pirko  *	@dev: port device
1910bc05d58SJiri Pirko  *	@attr: attribute to set
192dcbdf135SVladimir Oltean  *	@extack: netlink extended ack, for error message propagation
1930bc05d58SJiri Pirko  *
1940bc05d58SJiri Pirko  *	rtnl_lock must be held and must not be in atomic section,
1950bc05d58SJiri Pirko  *	in case SWITCHDEV_F_DEFER flag is not set.
1960bc05d58SJiri Pirko  */
1970bc05d58SJiri Pirko int switchdev_port_attr_set(struct net_device *dev,
198dcbdf135SVladimir Oltean 			    const struct switchdev_attr *attr,
199dcbdf135SVladimir Oltean 			    struct netlink_ext_ack *extack)
2000bc05d58SJiri Pirko {
2010bc05d58SJiri Pirko 	if (attr->flags & SWITCHDEV_F_DEFER)
2020bc05d58SJiri Pirko 		return switchdev_port_attr_set_defer(dev, attr);
2030bc05d58SJiri Pirko 	ASSERT_RTNL();
204dcbdf135SVladimir Oltean 	return switchdev_port_attr_set_now(dev, attr, extack);
2050bc05d58SJiri Pirko }
2063094333dSScott Feldman EXPORT_SYMBOL_GPL(switchdev_port_attr_set);
2073094333dSScott Feldman 
208e258d919SScott Feldman static size_t switchdev_obj_size(const struct switchdev_obj *obj)
209e258d919SScott Feldman {
210e258d919SScott Feldman 	switch (obj->id) {
211e258d919SScott Feldman 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
212e258d919SScott Feldman 		return sizeof(struct switchdev_obj_port_vlan);
2134d41e125SElad Raz 	case SWITCHDEV_OBJ_ID_PORT_MDB:
2144d41e125SElad Raz 		return sizeof(struct switchdev_obj_port_mdb);
21547d5b6dbSAndrew Lunn 	case SWITCHDEV_OBJ_ID_HOST_MDB:
21647d5b6dbSAndrew Lunn 		return sizeof(struct switchdev_obj_port_mdb);
217e258d919SScott Feldman 	default:
218e258d919SScott Feldman 		BUG();
219e258d919SScott Feldman 	}
220e258d919SScott Feldman 	return 0;
221e258d919SScott Feldman }
222e258d919SScott Feldman 
223d17d9f5eSPetr Machata static int switchdev_port_obj_notify(enum switchdev_notifier_type nt,
224d17d9f5eSPetr Machata 				     struct net_device *dev,
225648b4a99SJiri Pirko 				     const struct switchdev_obj *obj,
22669b7320eSPetr Machata 				     struct netlink_ext_ack *extack)
227491d0f15SScott Feldman {
228d17d9f5eSPetr Machata 	int rc;
229d17d9f5eSPetr Machata 	int err;
230491d0f15SScott Feldman 
231d17d9f5eSPetr Machata 	struct switchdev_notifier_port_obj_info obj_info = {
232d17d9f5eSPetr Machata 		.obj = obj,
233d17d9f5eSPetr Machata 		.handled = false,
234d17d9f5eSPetr Machata 	};
235491d0f15SScott Feldman 
236479c86dcSPetr Machata 	rc = call_switchdev_blocking_notifiers(nt, dev, &obj_info.info, extack);
237d17d9f5eSPetr Machata 	err = notifier_to_errno(rc);
238d17d9f5eSPetr Machata 	if (err) {
239d17d9f5eSPetr Machata 		WARN_ON(!obj_info.handled);
240491d0f15SScott Feldman 		return err;
241491d0f15SScott Feldman 	}
242d17d9f5eSPetr Machata 	if (!obj_info.handled)
243d17d9f5eSPetr Machata 		return -EOPNOTSUPP;
244d17d9f5eSPetr Machata 	return 0;
245d17d9f5eSPetr Machata }
246491d0f15SScott Feldman 
2474d429c5dSJiri Pirko static void switchdev_port_obj_add_deferred(struct net_device *dev,
2484d429c5dSJiri Pirko 					    const void *data)
2494d429c5dSJiri Pirko {
2504d429c5dSJiri Pirko 	const struct switchdev_obj *obj = data;
2514d429c5dSJiri Pirko 	int err;
2524d429c5dSJiri Pirko 
253cf6def51SVladimir Oltean 	ASSERT_RTNL();
254cf6def51SVladimir Oltean 	err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD,
255cf6def51SVladimir Oltean 					dev, obj, NULL);
2564d429c5dSJiri Pirko 	if (err && err != -EOPNOTSUPP)
2574d429c5dSJiri Pirko 		netdev_err(dev, "failed (err=%d) to add object (id=%d)\n",
2584d429c5dSJiri Pirko 			   err, obj->id);
2597ceb2afbSElad Raz 	if (obj->complete)
2607ceb2afbSElad Raz 		obj->complete(dev, err, obj->complete_priv);
2614d429c5dSJiri Pirko }
2624d429c5dSJiri Pirko 
2634d429c5dSJiri Pirko static int switchdev_port_obj_add_defer(struct net_device *dev,
2644d429c5dSJiri Pirko 					const struct switchdev_obj *obj)
2654d429c5dSJiri Pirko {
266e258d919SScott Feldman 	return switchdev_deferred_enqueue(dev, obj, switchdev_obj_size(obj),
2674d429c5dSJiri Pirko 					  switchdev_port_obj_add_deferred);
2684d429c5dSJiri Pirko }
269491d0f15SScott Feldman 
270491d0f15SScott Feldman /**
2714d429c5dSJiri Pirko  *	switchdev_port_obj_add - Add port object
272491d0f15SScott Feldman  *
273491d0f15SScott Feldman  *	@dev: port device
2744d429c5dSJiri Pirko  *	@obj: object to add
275c8af73f0SAndrew Lunn  *	@extack: netlink extended ack
2764d429c5dSJiri Pirko  *
2774d429c5dSJiri Pirko  *	rtnl_lock must be held and must not be in atomic section,
2784d429c5dSJiri Pirko  *	in case SWITCHDEV_F_DEFER flag is not set.
279491d0f15SScott Feldman  */
2804d429c5dSJiri Pirko int switchdev_port_obj_add(struct net_device *dev,
28169b7320eSPetr Machata 			   const struct switchdev_obj *obj,
28269b7320eSPetr Machata 			   struct netlink_ext_ack *extack)
2834d429c5dSJiri Pirko {
2844d429c5dSJiri Pirko 	if (obj->flags & SWITCHDEV_F_DEFER)
2854d429c5dSJiri Pirko 		return switchdev_port_obj_add_defer(dev, obj);
2864d429c5dSJiri Pirko 	ASSERT_RTNL();
287cf6def51SVladimir Oltean 	return switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD,
288cf6def51SVladimir Oltean 					 dev, obj, extack);
2894d429c5dSJiri Pirko }
2904d429c5dSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_port_obj_add);
2914d429c5dSJiri Pirko 
2924d429c5dSJiri Pirko static int switchdev_port_obj_del_now(struct net_device *dev,
293648b4a99SJiri Pirko 				      const struct switchdev_obj *obj)
294491d0f15SScott Feldman {
295d17d9f5eSPetr Machata 	return switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_DEL,
296ffb68fc5SVladimir Oltean 					 dev, obj, NULL);
297491d0f15SScott Feldman }
2984d429c5dSJiri Pirko 
2994d429c5dSJiri Pirko static void switchdev_port_obj_del_deferred(struct net_device *dev,
3004d429c5dSJiri Pirko 					    const void *data)
3014d429c5dSJiri Pirko {
3024d429c5dSJiri Pirko 	const struct switchdev_obj *obj = data;
3034d429c5dSJiri Pirko 	int err;
3044d429c5dSJiri Pirko 
3054d429c5dSJiri Pirko 	err = switchdev_port_obj_del_now(dev, obj);
3064d429c5dSJiri Pirko 	if (err && err != -EOPNOTSUPP)
3074d429c5dSJiri Pirko 		netdev_err(dev, "failed (err=%d) to del object (id=%d)\n",
3084d429c5dSJiri Pirko 			   err, obj->id);
3097ceb2afbSElad Raz 	if (obj->complete)
3107ceb2afbSElad Raz 		obj->complete(dev, err, obj->complete_priv);
3114d429c5dSJiri Pirko }
3124d429c5dSJiri Pirko 
3134d429c5dSJiri Pirko static int switchdev_port_obj_del_defer(struct net_device *dev,
3144d429c5dSJiri Pirko 					const struct switchdev_obj *obj)
3154d429c5dSJiri Pirko {
316e258d919SScott Feldman 	return switchdev_deferred_enqueue(dev, obj, switchdev_obj_size(obj),
3174d429c5dSJiri Pirko 					  switchdev_port_obj_del_deferred);
3184d429c5dSJiri Pirko }
3194d429c5dSJiri Pirko 
3204d429c5dSJiri Pirko /**
3214d429c5dSJiri Pirko  *	switchdev_port_obj_del - Delete port object
3224d429c5dSJiri Pirko  *
3234d429c5dSJiri Pirko  *	@dev: port device
3244d429c5dSJiri Pirko  *	@obj: object to delete
3254d429c5dSJiri Pirko  *
3264d429c5dSJiri Pirko  *	rtnl_lock must be held and must not be in atomic section,
3274d429c5dSJiri Pirko  *	in case SWITCHDEV_F_DEFER flag is not set.
3284d429c5dSJiri Pirko  */
3294d429c5dSJiri Pirko int switchdev_port_obj_del(struct net_device *dev,
3304d429c5dSJiri Pirko 			   const struct switchdev_obj *obj)
3314d429c5dSJiri Pirko {
3324d429c5dSJiri Pirko 	if (obj->flags & SWITCHDEV_F_DEFER)
3334d429c5dSJiri Pirko 		return switchdev_port_obj_del_defer(dev, obj);
3344d429c5dSJiri Pirko 	ASSERT_RTNL();
3354d429c5dSJiri Pirko 	return switchdev_port_obj_del_now(dev, obj);
3364d429c5dSJiri Pirko }
337491d0f15SScott Feldman EXPORT_SYMBOL_GPL(switchdev_port_obj_del);
338491d0f15SScott Feldman 
339*dc489f86STobias Waldekranz /**
340*dc489f86STobias Waldekranz  *	switchdev_port_obj_act_is_deferred - Is object action pending?
341*dc489f86STobias Waldekranz  *
342*dc489f86STobias Waldekranz  *	@dev: port device
343*dc489f86STobias Waldekranz  *	@nt: type of action; add or delete
344*dc489f86STobias Waldekranz  *	@obj: object to test
345*dc489f86STobias Waldekranz  *
346*dc489f86STobias Waldekranz  *	Returns true if a deferred item is pending, which is
347*dc489f86STobias Waldekranz  *	equivalent to the action @nt on an object @obj.
348*dc489f86STobias Waldekranz  *
349*dc489f86STobias Waldekranz  *	rtnl_lock must be held.
350*dc489f86STobias Waldekranz  */
351*dc489f86STobias Waldekranz bool switchdev_port_obj_act_is_deferred(struct net_device *dev,
352*dc489f86STobias Waldekranz 					enum switchdev_notifier_type nt,
353*dc489f86STobias Waldekranz 					const struct switchdev_obj *obj)
354*dc489f86STobias Waldekranz {
355*dc489f86STobias Waldekranz 	struct switchdev_deferred_item *dfitem;
356*dc489f86STobias Waldekranz 	bool found = false;
357*dc489f86STobias Waldekranz 
358*dc489f86STobias Waldekranz 	ASSERT_RTNL();
359*dc489f86STobias Waldekranz 
360*dc489f86STobias Waldekranz 	spin_lock_bh(&deferred_lock);
361*dc489f86STobias Waldekranz 
362*dc489f86STobias Waldekranz 	list_for_each_entry(dfitem, &deferred, list) {
363*dc489f86STobias Waldekranz 		if (dfitem->dev != dev)
364*dc489f86STobias Waldekranz 			continue;
365*dc489f86STobias Waldekranz 
366*dc489f86STobias Waldekranz 		if ((dfitem->func == switchdev_port_obj_add_deferred &&
367*dc489f86STobias Waldekranz 		     nt == SWITCHDEV_PORT_OBJ_ADD) ||
368*dc489f86STobias Waldekranz 		    (dfitem->func == switchdev_port_obj_del_deferred &&
369*dc489f86STobias Waldekranz 		     nt == SWITCHDEV_PORT_OBJ_DEL)) {
370*dc489f86STobias Waldekranz 			if (switchdev_obj_eq((const void *)dfitem->data, obj)) {
371*dc489f86STobias Waldekranz 				found = true;
372*dc489f86STobias Waldekranz 				break;
373*dc489f86STobias Waldekranz 			}
374*dc489f86STobias Waldekranz 		}
375*dc489f86STobias Waldekranz 	}
376*dc489f86STobias Waldekranz 
377*dc489f86STobias Waldekranz 	spin_unlock_bh(&deferred_lock);
378*dc489f86STobias Waldekranz 
379*dc489f86STobias Waldekranz 	return found;
380*dc489f86STobias Waldekranz }
381*dc489f86STobias Waldekranz EXPORT_SYMBOL_GPL(switchdev_port_obj_act_is_deferred);
382*dc489f86STobias Waldekranz 
383ff5cf100SArkadi Sharshevsky static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain);
384a93e3b17SPetr Machata static BLOCKING_NOTIFIER_HEAD(switchdev_blocking_notif_chain);
38503bf0c28SJiri Pirko 
38603bf0c28SJiri Pirko /**
387ebb9a03aSJiri Pirko  *	register_switchdev_notifier - Register notifier
38803bf0c28SJiri Pirko  *	@nb: notifier_block
38903bf0c28SJiri Pirko  *
390ff5cf100SArkadi Sharshevsky  *	Register switch device notifier.
39103bf0c28SJiri Pirko  */
392ebb9a03aSJiri Pirko int register_switchdev_notifier(struct notifier_block *nb)
39303bf0c28SJiri Pirko {
394ff5cf100SArkadi Sharshevsky 	return atomic_notifier_chain_register(&switchdev_notif_chain, nb);
39503bf0c28SJiri Pirko }
396ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(register_switchdev_notifier);
39703bf0c28SJiri Pirko 
39803bf0c28SJiri Pirko /**
399ebb9a03aSJiri Pirko  *	unregister_switchdev_notifier - Unregister notifier
40003bf0c28SJiri Pirko  *	@nb: notifier_block
40103bf0c28SJiri Pirko  *
40203bf0c28SJiri Pirko  *	Unregister switch device notifier.
40303bf0c28SJiri Pirko  */
404ebb9a03aSJiri Pirko int unregister_switchdev_notifier(struct notifier_block *nb)
40503bf0c28SJiri Pirko {
406ff5cf100SArkadi Sharshevsky 	return atomic_notifier_chain_unregister(&switchdev_notif_chain, nb);
40703bf0c28SJiri Pirko }
408ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(unregister_switchdev_notifier);
40903bf0c28SJiri Pirko 
41003bf0c28SJiri Pirko /**
411ebb9a03aSJiri Pirko  *	call_switchdev_notifiers - Call notifiers
41203bf0c28SJiri Pirko  *	@val: value passed unmodified to notifier function
41303bf0c28SJiri Pirko  *	@dev: port device
41403bf0c28SJiri Pirko  *	@info: notifier information data
415ea6754aeSTian Tao  *	@extack: netlink extended ack
416ff5cf100SArkadi Sharshevsky  *	Call all network notifier blocks.
41703bf0c28SJiri Pirko  */
418ebb9a03aSJiri Pirko int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
4196685987cSPetr Machata 			     struct switchdev_notifier_info *info,
4206685987cSPetr Machata 			     struct netlink_ext_ack *extack)
42103bf0c28SJiri Pirko {
42203bf0c28SJiri Pirko 	info->dev = dev;
4236685987cSPetr Machata 	info->extack = extack;
424ff5cf100SArkadi Sharshevsky 	return atomic_notifier_call_chain(&switchdev_notif_chain, val, info);
42503bf0c28SJiri Pirko }
426ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
4278a44dbb2SRoopa Prabhu 
428a93e3b17SPetr Machata int register_switchdev_blocking_notifier(struct notifier_block *nb)
429a93e3b17SPetr Machata {
430a93e3b17SPetr Machata 	struct blocking_notifier_head *chain = &switchdev_blocking_notif_chain;
431a93e3b17SPetr Machata 
432a93e3b17SPetr Machata 	return blocking_notifier_chain_register(chain, nb);
433a93e3b17SPetr Machata }
434a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(register_switchdev_blocking_notifier);
435a93e3b17SPetr Machata 
436a93e3b17SPetr Machata int unregister_switchdev_blocking_notifier(struct notifier_block *nb)
437a93e3b17SPetr Machata {
438a93e3b17SPetr Machata 	struct blocking_notifier_head *chain = &switchdev_blocking_notif_chain;
439a93e3b17SPetr Machata 
440a93e3b17SPetr Machata 	return blocking_notifier_chain_unregister(chain, nb);
441a93e3b17SPetr Machata }
442a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(unregister_switchdev_blocking_notifier);
443a93e3b17SPetr Machata 
444a93e3b17SPetr Machata int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev,
445479c86dcSPetr Machata 				      struct switchdev_notifier_info *info,
446479c86dcSPetr Machata 				      struct netlink_ext_ack *extack)
447a93e3b17SPetr Machata {
448a93e3b17SPetr Machata 	info->dev = dev;
449479c86dcSPetr Machata 	info->extack = extack;
450a93e3b17SPetr Machata 	return blocking_notifier_call_chain(&switchdev_blocking_notif_chain,
451a93e3b17SPetr Machata 					    val, info);
452a93e3b17SPetr Machata }
453a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(call_switchdev_blocking_notifiers);
454a93e3b17SPetr Machata 
4552b0a5688SVladimir Oltean struct switchdev_nested_priv {
4562b0a5688SVladimir Oltean 	bool (*check_cb)(const struct net_device *dev);
4572b0a5688SVladimir Oltean 	bool (*foreign_dev_check_cb)(const struct net_device *dev,
4582b0a5688SVladimir Oltean 				     const struct net_device *foreign_dev);
4592b0a5688SVladimir Oltean 	const struct net_device *dev;
4602b0a5688SVladimir Oltean 	struct net_device *lower_dev;
4612b0a5688SVladimir Oltean };
4622b0a5688SVladimir Oltean 
4632b0a5688SVladimir Oltean static int switchdev_lower_dev_walk(struct net_device *lower_dev,
4642b0a5688SVladimir Oltean 				    struct netdev_nested_priv *priv)
4652b0a5688SVladimir Oltean {
4662b0a5688SVladimir Oltean 	struct switchdev_nested_priv *switchdev_priv = priv->data;
4672b0a5688SVladimir Oltean 	bool (*foreign_dev_check_cb)(const struct net_device *dev,
4682b0a5688SVladimir Oltean 				     const struct net_device *foreign_dev);
4692b0a5688SVladimir Oltean 	bool (*check_cb)(const struct net_device *dev);
4702b0a5688SVladimir Oltean 	const struct net_device *dev;
4712b0a5688SVladimir Oltean 
4722b0a5688SVladimir Oltean 	check_cb = switchdev_priv->check_cb;
4732b0a5688SVladimir Oltean 	foreign_dev_check_cb = switchdev_priv->foreign_dev_check_cb;
4742b0a5688SVladimir Oltean 	dev = switchdev_priv->dev;
4752b0a5688SVladimir Oltean 
4762b0a5688SVladimir Oltean 	if (check_cb(lower_dev) && !foreign_dev_check_cb(lower_dev, dev)) {
4772b0a5688SVladimir Oltean 		switchdev_priv->lower_dev = lower_dev;
4782b0a5688SVladimir Oltean 		return 1;
4792b0a5688SVladimir Oltean 	}
4802b0a5688SVladimir Oltean 
4812b0a5688SVladimir Oltean 	return 0;
4822b0a5688SVladimir Oltean }
4832b0a5688SVladimir Oltean 
4842b0a5688SVladimir Oltean static struct net_device *
4857b465f4cSVladimir Oltean switchdev_lower_dev_find_rcu(struct net_device *dev,
4862b0a5688SVladimir Oltean 			     bool (*check_cb)(const struct net_device *dev),
4872b0a5688SVladimir Oltean 			     bool (*foreign_dev_check_cb)(const struct net_device *dev,
4882b0a5688SVladimir Oltean 							  const struct net_device *foreign_dev))
4892b0a5688SVladimir Oltean {
4902b0a5688SVladimir Oltean 	struct switchdev_nested_priv switchdev_priv = {
4912b0a5688SVladimir Oltean 		.check_cb = check_cb,
4922b0a5688SVladimir Oltean 		.foreign_dev_check_cb = foreign_dev_check_cb,
4932b0a5688SVladimir Oltean 		.dev = dev,
4942b0a5688SVladimir Oltean 		.lower_dev = NULL,
4952b0a5688SVladimir Oltean 	};
4962b0a5688SVladimir Oltean 	struct netdev_nested_priv priv = {
4972b0a5688SVladimir Oltean 		.data = &switchdev_priv,
4982b0a5688SVladimir Oltean 	};
4992b0a5688SVladimir Oltean 
5002b0a5688SVladimir Oltean 	netdev_walk_all_lower_dev_rcu(dev, switchdev_lower_dev_walk, &priv);
5012b0a5688SVladimir Oltean 
5022b0a5688SVladimir Oltean 	return switchdev_priv.lower_dev;
5032b0a5688SVladimir Oltean }
5042b0a5688SVladimir Oltean 
505c4076cddSVladimir Oltean static struct net_device *
506c4076cddSVladimir Oltean switchdev_lower_dev_find(struct net_device *dev,
507c4076cddSVladimir Oltean 			 bool (*check_cb)(const struct net_device *dev),
508c4076cddSVladimir Oltean 			 bool (*foreign_dev_check_cb)(const struct net_device *dev,
509c4076cddSVladimir Oltean 						      const struct net_device *foreign_dev))
510c4076cddSVladimir Oltean {
511c4076cddSVladimir Oltean 	struct switchdev_nested_priv switchdev_priv = {
512c4076cddSVladimir Oltean 		.check_cb = check_cb,
513c4076cddSVladimir Oltean 		.foreign_dev_check_cb = foreign_dev_check_cb,
514c4076cddSVladimir Oltean 		.dev = dev,
515c4076cddSVladimir Oltean 		.lower_dev = NULL,
516c4076cddSVladimir Oltean 	};
517c4076cddSVladimir Oltean 	struct netdev_nested_priv priv = {
518c4076cddSVladimir Oltean 		.data = &switchdev_priv,
519c4076cddSVladimir Oltean 	};
520c4076cddSVladimir Oltean 
521c4076cddSVladimir Oltean 	netdev_walk_all_lower_dev(dev, switchdev_lower_dev_walk, &priv);
522c4076cddSVladimir Oltean 
523c4076cddSVladimir Oltean 	return switchdev_priv.lower_dev;
524c4076cddSVladimir Oltean }
525c4076cddSVladimir Oltean 
526716a30a9SVladimir Oltean static int __switchdev_handle_fdb_event_to_device(struct net_device *dev,
527716a30a9SVladimir Oltean 		struct net_device *orig_dev, unsigned long event,
5288ca07176SVladimir Oltean 		const struct switchdev_notifier_fdb_info *fdb_info,
5298ca07176SVladimir Oltean 		bool (*check_cb)(const struct net_device *dev),
5308ca07176SVladimir Oltean 		bool (*foreign_dev_check_cb)(const struct net_device *dev,
5318ca07176SVladimir Oltean 					     const struct net_device *foreign_dev),
532716a30a9SVladimir Oltean 		int (*mod_cb)(struct net_device *dev, struct net_device *orig_dev,
533716a30a9SVladimir Oltean 			      unsigned long event, const void *ctx,
5348ca07176SVladimir Oltean 			      const struct switchdev_notifier_fdb_info *fdb_info))
5358ca07176SVladimir Oltean {
5368ca07176SVladimir Oltean 	const struct switchdev_notifier_info *info = &fdb_info->info;
537ec638740SVladimir Oltean 	struct net_device *br, *lower_dev, *switchdev;
5388ca07176SVladimir Oltean 	struct list_head *iter;
5398ca07176SVladimir Oltean 	int err = -EOPNOTSUPP;
5408ca07176SVladimir Oltean 
5412b0a5688SVladimir Oltean 	if (check_cb(dev))
542716a30a9SVladimir Oltean 		return mod_cb(dev, orig_dev, event, info->ctx, fdb_info);
5438ca07176SVladimir Oltean 
5448ca07176SVladimir Oltean 	/* Recurse through lower interfaces in case the FDB entry is pointing
545ec638740SVladimir Oltean 	 * towards a bridge or a LAG device.
5468ca07176SVladimir Oltean 	 */
5478ca07176SVladimir Oltean 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
5488ca07176SVladimir Oltean 		/* Do not propagate FDB entries across bridges */
5498ca07176SVladimir Oltean 		if (netif_is_bridge_master(lower_dev))
5508ca07176SVladimir Oltean 			continue;
5518ca07176SVladimir Oltean 
5522b0a5688SVladimir Oltean 		/* Bridge ports might be either us, or LAG interfaces
5532b0a5688SVladimir Oltean 		 * that we offload.
5542b0a5688SVladimir Oltean 		 */
5552b0a5688SVladimir Oltean 		if (!check_cb(lower_dev) &&
5567b465f4cSVladimir Oltean 		    !switchdev_lower_dev_find_rcu(lower_dev, check_cb,
5572b0a5688SVladimir Oltean 						  foreign_dev_check_cb))
5582b0a5688SVladimir Oltean 			continue;
5592b0a5688SVladimir Oltean 
560716a30a9SVladimir Oltean 		err = __switchdev_handle_fdb_event_to_device(lower_dev, orig_dev,
561716a30a9SVladimir Oltean 							     event, fdb_info, check_cb,
5628ca07176SVladimir Oltean 							     foreign_dev_check_cb,
563ec638740SVladimir Oltean 							     mod_cb);
5648ca07176SVladimir Oltean 		if (err && err != -EOPNOTSUPP)
5658ca07176SVladimir Oltean 			return err;
5668ca07176SVladimir Oltean 	}
5678ca07176SVladimir Oltean 
5682b0a5688SVladimir Oltean 	/* Event is neither on a bridge nor a LAG. Check whether it is on an
5692b0a5688SVladimir Oltean 	 * interface that is in a bridge with us.
5702b0a5688SVladimir Oltean 	 */
5712b0a5688SVladimir Oltean 	br = netdev_master_upper_dev_get_rcu(dev);
5722b0a5688SVladimir Oltean 	if (!br || !netif_is_bridge_master(br))
5732b0a5688SVladimir Oltean 		return 0;
5742b0a5688SVladimir Oltean 
575ec638740SVladimir Oltean 	switchdev = switchdev_lower_dev_find_rcu(br, check_cb, foreign_dev_check_cb);
576ec638740SVladimir Oltean 	if (!switchdev)
5772b0a5688SVladimir Oltean 		return 0;
5782b0a5688SVladimir Oltean 
579ec638740SVladimir Oltean 	if (!foreign_dev_check_cb(switchdev, dev))
580ec638740SVladimir Oltean 		return err;
581ec638740SVladimir Oltean 
582716a30a9SVladimir Oltean 	return __switchdev_handle_fdb_event_to_device(br, orig_dev, event, fdb_info,
5832b0a5688SVladimir Oltean 						      check_cb, foreign_dev_check_cb,
584ec638740SVladimir Oltean 						      mod_cb);
5858ca07176SVladimir Oltean }
5868ca07176SVladimir Oltean 
587716a30a9SVladimir Oltean int switchdev_handle_fdb_event_to_device(struct net_device *dev, unsigned long event,
5888ca07176SVladimir Oltean 		const struct switchdev_notifier_fdb_info *fdb_info,
5898ca07176SVladimir Oltean 		bool (*check_cb)(const struct net_device *dev),
5908ca07176SVladimir Oltean 		bool (*foreign_dev_check_cb)(const struct net_device *dev,
5918ca07176SVladimir Oltean 					     const struct net_device *foreign_dev),
592716a30a9SVladimir Oltean 		int (*mod_cb)(struct net_device *dev, struct net_device *orig_dev,
593716a30a9SVladimir Oltean 			      unsigned long event, const void *ctx,
5948ca07176SVladimir Oltean 			      const struct switchdev_notifier_fdb_info *fdb_info))
5958ca07176SVladimir Oltean {
5968ca07176SVladimir Oltean 	int err;
5978ca07176SVladimir Oltean 
598716a30a9SVladimir Oltean 	err = __switchdev_handle_fdb_event_to_device(dev, dev, event, fdb_info,
599716a30a9SVladimir Oltean 						     check_cb, foreign_dev_check_cb,
600ec638740SVladimir Oltean 						     mod_cb);
6018ca07176SVladimir Oltean 	if (err == -EOPNOTSUPP)
6028ca07176SVladimir Oltean 		err = 0;
6038ca07176SVladimir Oltean 
6048ca07176SVladimir Oltean 	return err;
6058ca07176SVladimir Oltean }
606716a30a9SVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_handle_fdb_event_to_device);
6078ca07176SVladimir Oltean 
608f30f0601SPetr Machata static int __switchdev_handle_port_obj_add(struct net_device *dev,
609f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
610f30f0601SPetr Machata 			bool (*check_cb)(const struct net_device *dev),
611c4076cddSVladimir Oltean 			bool (*foreign_dev_check_cb)(const struct net_device *dev,
612c4076cddSVladimir Oltean 						     const struct net_device *foreign_dev),
61369bfac96SVladimir Oltean 			int (*add_cb)(struct net_device *dev, const void *ctx,
614f30f0601SPetr Machata 				      const struct switchdev_obj *obj,
61569213513SPetr Machata 				      struct netlink_ext_ack *extack))
616f30f0601SPetr Machata {
61769bfac96SVladimir Oltean 	struct switchdev_notifier_info *info = &port_obj_info->info;
618acd8df58SVladimir Oltean 	struct net_device *br, *lower_dev, *switchdev;
61969213513SPetr Machata 	struct netlink_ext_ack *extack;
620f30f0601SPetr Machata 	struct list_head *iter;
621f30f0601SPetr Machata 	int err = -EOPNOTSUPP;
622f30f0601SPetr Machata 
62369bfac96SVladimir Oltean 	extack = switchdev_notifier_info_to_extack(info);
62469213513SPetr Machata 
625f30f0601SPetr Machata 	if (check_cb(dev)) {
62669bfac96SVladimir Oltean 		err = add_cb(dev, info->ctx, port_obj_info->obj, extack);
62720776b46SRasmus Villemoes 		if (err != -EOPNOTSUPP)
628f30f0601SPetr Machata 			port_obj_info->handled = true;
62920776b46SRasmus Villemoes 		return err;
630f30f0601SPetr Machata 	}
631f30f0601SPetr Machata 
632f30f0601SPetr Machata 	/* Switch ports might be stacked under e.g. a LAG. Ignore the
633f30f0601SPetr Machata 	 * unsupported devices, another driver might be able to handle them. But
634f30f0601SPetr Machata 	 * propagate to the callers any hard errors.
635f30f0601SPetr Machata 	 *
636f30f0601SPetr Machata 	 * If the driver does its own bookkeeping of stacked ports, it's not
637f30f0601SPetr Machata 	 * necessary to go through this helper.
638f30f0601SPetr Machata 	 */
639f30f0601SPetr Machata 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
64007c6f980SRussell King 		if (netif_is_bridge_master(lower_dev))
64107c6f980SRussell King 			continue;
64207c6f980SRussell King 
643c4076cddSVladimir Oltean 		/* When searching for switchdev interfaces that are neighbors
644c4076cddSVladimir Oltean 		 * of foreign ones, and @dev is a bridge, do not recurse on the
645c4076cddSVladimir Oltean 		 * foreign interface again, it was already visited.
646c4076cddSVladimir Oltean 		 */
647c4076cddSVladimir Oltean 		if (foreign_dev_check_cb && !check_cb(lower_dev) &&
648c4076cddSVladimir Oltean 		    !switchdev_lower_dev_find(lower_dev, check_cb, foreign_dev_check_cb))
649c4076cddSVladimir Oltean 			continue;
650c4076cddSVladimir Oltean 
651f30f0601SPetr Machata 		err = __switchdev_handle_port_obj_add(lower_dev, port_obj_info,
652c4076cddSVladimir Oltean 						      check_cb, foreign_dev_check_cb,
653c4076cddSVladimir Oltean 						      add_cb);
654f30f0601SPetr Machata 		if (err && err != -EOPNOTSUPP)
655f30f0601SPetr Machata 			return err;
656f30f0601SPetr Machata 	}
657f30f0601SPetr Machata 
658c4076cddSVladimir Oltean 	/* Event is neither on a bridge nor a LAG. Check whether it is on an
659c4076cddSVladimir Oltean 	 * interface that is in a bridge with us.
660c4076cddSVladimir Oltean 	 */
661c4076cddSVladimir Oltean 	if (!foreign_dev_check_cb)
662f30f0601SPetr Machata 		return err;
663c4076cddSVladimir Oltean 
664c4076cddSVladimir Oltean 	br = netdev_master_upper_dev_get(dev);
665c4076cddSVladimir Oltean 	if (!br || !netif_is_bridge_master(br))
666c4076cddSVladimir Oltean 		return err;
667c4076cddSVladimir Oltean 
668acd8df58SVladimir Oltean 	switchdev = switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb);
669acd8df58SVladimir Oltean 	if (!switchdev)
670acd8df58SVladimir Oltean 		return err;
671acd8df58SVladimir Oltean 
672acd8df58SVladimir Oltean 	if (!foreign_dev_check_cb(switchdev, dev))
673c4076cddSVladimir Oltean 		return err;
674c4076cddSVladimir Oltean 
675c4076cddSVladimir Oltean 	return __switchdev_handle_port_obj_add(br, port_obj_info, check_cb,
676c4076cddSVladimir Oltean 					       foreign_dev_check_cb, add_cb);
677f30f0601SPetr Machata }
678f30f0601SPetr Machata 
679c4076cddSVladimir Oltean /* Pass through a port object addition, if @dev passes @check_cb, or replicate
680c4076cddSVladimir Oltean  * it towards all lower interfaces of @dev that pass @check_cb, if @dev is a
681c4076cddSVladimir Oltean  * bridge or a LAG.
682c4076cddSVladimir Oltean  */
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,
693c4076cddSVladimir Oltean 					      NULL, 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 
700c4076cddSVladimir Oltean /* Same as switchdev_handle_port_obj_add(), except if object is notified on a
701c4076cddSVladimir Oltean  * @dev that passes @foreign_dev_check_cb, it is replicated towards all devices
702c4076cddSVladimir Oltean  * that pass @check_cb and are in the same bridge as @dev.
703c4076cddSVladimir Oltean  */
704c4076cddSVladimir Oltean int switchdev_handle_port_obj_add_foreign(struct net_device *dev,
705c4076cddSVladimir Oltean 			struct switchdev_notifier_port_obj_info *port_obj_info,
706c4076cddSVladimir Oltean 			bool (*check_cb)(const struct net_device *dev),
707c4076cddSVladimir Oltean 			bool (*foreign_dev_check_cb)(const struct net_device *dev,
708c4076cddSVladimir Oltean 						     const struct net_device *foreign_dev),
709c4076cddSVladimir Oltean 			int (*add_cb)(struct net_device *dev, const void *ctx,
710c4076cddSVladimir Oltean 				      const struct switchdev_obj *obj,
711c4076cddSVladimir Oltean 				      struct netlink_ext_ack *extack))
712c4076cddSVladimir Oltean {
713c4076cddSVladimir Oltean 	int err;
714c4076cddSVladimir Oltean 
715c4076cddSVladimir Oltean 	err = __switchdev_handle_port_obj_add(dev, port_obj_info, check_cb,
716c4076cddSVladimir Oltean 					      foreign_dev_check_cb, add_cb);
717c4076cddSVladimir Oltean 	if (err == -EOPNOTSUPP)
718c4076cddSVladimir Oltean 		err = 0;
719c4076cddSVladimir Oltean 	return err;
720c4076cddSVladimir Oltean }
721c4076cddSVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add_foreign);
722c4076cddSVladimir Oltean 
723f30f0601SPetr Machata static int __switchdev_handle_port_obj_del(struct net_device *dev,
724f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
725f30f0601SPetr Machata 			bool (*check_cb)(const struct net_device *dev),
726c4076cddSVladimir Oltean 			bool (*foreign_dev_check_cb)(const struct net_device *dev,
727c4076cddSVladimir Oltean 						     const struct net_device *foreign_dev),
72869bfac96SVladimir Oltean 			int (*del_cb)(struct net_device *dev, const void *ctx,
729f30f0601SPetr Machata 				      const struct switchdev_obj *obj))
730f30f0601SPetr Machata {
73169bfac96SVladimir Oltean 	struct switchdev_notifier_info *info = &port_obj_info->info;
732acd8df58SVladimir Oltean 	struct net_device *br, *lower_dev, *switchdev;
733f30f0601SPetr Machata 	struct list_head *iter;
734f30f0601SPetr Machata 	int err = -EOPNOTSUPP;
735f30f0601SPetr Machata 
736f30f0601SPetr Machata 	if (check_cb(dev)) {
73769bfac96SVladimir Oltean 		err = del_cb(dev, info->ctx, port_obj_info->obj);
73820776b46SRasmus Villemoes 		if (err != -EOPNOTSUPP)
739f30f0601SPetr Machata 			port_obj_info->handled = true;
74020776b46SRasmus Villemoes 		return err;
741f30f0601SPetr Machata 	}
742f30f0601SPetr Machata 
743f30f0601SPetr Machata 	/* Switch ports might be stacked under e.g. a LAG. Ignore the
744f30f0601SPetr Machata 	 * unsupported devices, another driver might be able to handle them. But
745f30f0601SPetr Machata 	 * propagate to the callers any hard errors.
746f30f0601SPetr Machata 	 *
747f30f0601SPetr Machata 	 * If the driver does its own bookkeeping of stacked ports, it's not
748f30f0601SPetr Machata 	 * necessary to go through this helper.
749f30f0601SPetr Machata 	 */
750f30f0601SPetr Machata 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
75107c6f980SRussell King 		if (netif_is_bridge_master(lower_dev))
75207c6f980SRussell King 			continue;
75307c6f980SRussell King 
754c4076cddSVladimir Oltean 		/* When searching for switchdev interfaces that are neighbors
755c4076cddSVladimir Oltean 		 * of foreign ones, and @dev is a bridge, do not recurse on the
756c4076cddSVladimir Oltean 		 * foreign interface again, it was already visited.
757c4076cddSVladimir Oltean 		 */
758c4076cddSVladimir Oltean 		if (foreign_dev_check_cb && !check_cb(lower_dev) &&
759c4076cddSVladimir Oltean 		    !switchdev_lower_dev_find(lower_dev, check_cb, foreign_dev_check_cb))
760c4076cddSVladimir Oltean 			continue;
761c4076cddSVladimir Oltean 
762f30f0601SPetr Machata 		err = __switchdev_handle_port_obj_del(lower_dev, port_obj_info,
763c4076cddSVladimir Oltean 						      check_cb, foreign_dev_check_cb,
764c4076cddSVladimir Oltean 						      del_cb);
765f30f0601SPetr Machata 		if (err && err != -EOPNOTSUPP)
766f30f0601SPetr Machata 			return err;
767f30f0601SPetr Machata 	}
768f30f0601SPetr Machata 
769c4076cddSVladimir Oltean 	/* Event is neither on a bridge nor a LAG. Check whether it is on an
770c4076cddSVladimir Oltean 	 * interface that is in a bridge with us.
771c4076cddSVladimir Oltean 	 */
772c4076cddSVladimir Oltean 	if (!foreign_dev_check_cb)
773f30f0601SPetr Machata 		return err;
774c4076cddSVladimir Oltean 
775c4076cddSVladimir Oltean 	br = netdev_master_upper_dev_get(dev);
776c4076cddSVladimir Oltean 	if (!br || !netif_is_bridge_master(br))
777c4076cddSVladimir Oltean 		return err;
778c4076cddSVladimir Oltean 
779acd8df58SVladimir Oltean 	switchdev = switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb);
780acd8df58SVladimir Oltean 	if (!switchdev)
781acd8df58SVladimir Oltean 		return err;
782acd8df58SVladimir Oltean 
783acd8df58SVladimir Oltean 	if (!foreign_dev_check_cb(switchdev, dev))
784c4076cddSVladimir Oltean 		return err;
785c4076cddSVladimir Oltean 
786c4076cddSVladimir Oltean 	return __switchdev_handle_port_obj_del(br, port_obj_info, check_cb,
787c4076cddSVladimir Oltean 					       foreign_dev_check_cb, del_cb);
788f30f0601SPetr Machata }
789f30f0601SPetr Machata 
790c4076cddSVladimir Oltean /* Pass through a port object deletion, if @dev passes @check_cb, or replicate
791c4076cddSVladimir Oltean  * it towards all lower interfaces of @dev that pass @check_cb, if @dev is a
792c4076cddSVladimir Oltean  * bridge or a LAG.
793c4076cddSVladimir Oltean  */
794f30f0601SPetr Machata int switchdev_handle_port_obj_del(struct net_device *dev,
795f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
796f30f0601SPetr Machata 			bool (*check_cb)(const struct net_device *dev),
79769bfac96SVladimir Oltean 			int (*del_cb)(struct net_device *dev, const void *ctx,
798f30f0601SPetr Machata 				      const struct switchdev_obj *obj))
799f30f0601SPetr Machata {
800f30f0601SPetr Machata 	int err;
801f30f0601SPetr Machata 
802f30f0601SPetr Machata 	err = __switchdev_handle_port_obj_del(dev, port_obj_info, check_cb,
803c4076cddSVladimir Oltean 					      NULL, del_cb);
804f30f0601SPetr Machata 	if (err == -EOPNOTSUPP)
805f30f0601SPetr Machata 		err = 0;
806f30f0601SPetr Machata 	return err;
807f30f0601SPetr Machata }
808f30f0601SPetr Machata EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del);
8091cb33af1SFlorian Fainelli 
810c4076cddSVladimir Oltean /* Same as switchdev_handle_port_obj_del(), except if object is notified on a
811c4076cddSVladimir Oltean  * @dev that passes @foreign_dev_check_cb, it is replicated towards all devices
812c4076cddSVladimir Oltean  * that pass @check_cb and are in the same bridge as @dev.
813c4076cddSVladimir Oltean  */
814c4076cddSVladimir Oltean int switchdev_handle_port_obj_del_foreign(struct net_device *dev,
815c4076cddSVladimir Oltean 			struct switchdev_notifier_port_obj_info *port_obj_info,
816c4076cddSVladimir Oltean 			bool (*check_cb)(const struct net_device *dev),
817c4076cddSVladimir Oltean 			bool (*foreign_dev_check_cb)(const struct net_device *dev,
818c4076cddSVladimir Oltean 						     const struct net_device *foreign_dev),
819c4076cddSVladimir Oltean 			int (*del_cb)(struct net_device *dev, const void *ctx,
820c4076cddSVladimir Oltean 				      const struct switchdev_obj *obj))
821c4076cddSVladimir Oltean {
822c4076cddSVladimir Oltean 	int err;
823c4076cddSVladimir Oltean 
824c4076cddSVladimir Oltean 	err = __switchdev_handle_port_obj_del(dev, port_obj_info, check_cb,
825c4076cddSVladimir Oltean 					      foreign_dev_check_cb, del_cb);
826c4076cddSVladimir Oltean 	if (err == -EOPNOTSUPP)
827c4076cddSVladimir Oltean 		err = 0;
828c4076cddSVladimir Oltean 	return err;
829c4076cddSVladimir Oltean }
830c4076cddSVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del_foreign);
831c4076cddSVladimir Oltean 
8321cb33af1SFlorian Fainelli static int __switchdev_handle_port_attr_set(struct net_device *dev,
8331cb33af1SFlorian Fainelli 			struct switchdev_notifier_port_attr_info *port_attr_info,
8341cb33af1SFlorian Fainelli 			bool (*check_cb)(const struct net_device *dev),
83569bfac96SVladimir Oltean 			int (*set_cb)(struct net_device *dev, const void *ctx,
8364c08c586SVladimir Oltean 				      const struct switchdev_attr *attr,
8374c08c586SVladimir Oltean 				      struct netlink_ext_ack *extack))
8381cb33af1SFlorian Fainelli {
83969bfac96SVladimir Oltean 	struct switchdev_notifier_info *info = &port_attr_info->info;
8404c08c586SVladimir Oltean 	struct netlink_ext_ack *extack;
8411cb33af1SFlorian Fainelli 	struct net_device *lower_dev;
8421cb33af1SFlorian Fainelli 	struct list_head *iter;
8431cb33af1SFlorian Fainelli 	int err = -EOPNOTSUPP;
8441cb33af1SFlorian Fainelli 
84569bfac96SVladimir Oltean 	extack = switchdev_notifier_info_to_extack(info);
8464c08c586SVladimir Oltean 
8471cb33af1SFlorian Fainelli 	if (check_cb(dev)) {
84869bfac96SVladimir Oltean 		err = set_cb(dev, info->ctx, port_attr_info->attr, extack);
84920776b46SRasmus Villemoes 		if (err != -EOPNOTSUPP)
8501cb33af1SFlorian Fainelli 			port_attr_info->handled = true;
85120776b46SRasmus Villemoes 		return err;
8521cb33af1SFlorian Fainelli 	}
8531cb33af1SFlorian Fainelli 
8541cb33af1SFlorian Fainelli 	/* Switch ports might be stacked under e.g. a LAG. Ignore the
8551cb33af1SFlorian Fainelli 	 * unsupported devices, another driver might be able to handle them. But
8561cb33af1SFlorian Fainelli 	 * propagate to the callers any hard errors.
8571cb33af1SFlorian Fainelli 	 *
8581cb33af1SFlorian Fainelli 	 * If the driver does its own bookkeeping of stacked ports, it's not
8591cb33af1SFlorian Fainelli 	 * necessary to go through this helper.
8601cb33af1SFlorian Fainelli 	 */
8611cb33af1SFlorian Fainelli 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
86207c6f980SRussell King 		if (netif_is_bridge_master(lower_dev))
86307c6f980SRussell King 			continue;
86407c6f980SRussell King 
8651cb33af1SFlorian Fainelli 		err = __switchdev_handle_port_attr_set(lower_dev, port_attr_info,
8661cb33af1SFlorian Fainelli 						       check_cb, set_cb);
8671cb33af1SFlorian Fainelli 		if (err && err != -EOPNOTSUPP)
8681cb33af1SFlorian Fainelli 			return err;
8691cb33af1SFlorian Fainelli 	}
8701cb33af1SFlorian Fainelli 
8711cb33af1SFlorian Fainelli 	return err;
8721cb33af1SFlorian Fainelli }
8731cb33af1SFlorian Fainelli 
8741cb33af1SFlorian Fainelli int switchdev_handle_port_attr_set(struct net_device *dev,
8751cb33af1SFlorian Fainelli 			struct switchdev_notifier_port_attr_info *port_attr_info,
8761cb33af1SFlorian Fainelli 			bool (*check_cb)(const struct net_device *dev),
87769bfac96SVladimir Oltean 			int (*set_cb)(struct net_device *dev, const void *ctx,
8784c08c586SVladimir Oltean 				      const struct switchdev_attr *attr,
8794c08c586SVladimir Oltean 				      struct netlink_ext_ack *extack))
8801cb33af1SFlorian Fainelli {
8811cb33af1SFlorian Fainelli 	int err;
8821cb33af1SFlorian Fainelli 
8831cb33af1SFlorian Fainelli 	err = __switchdev_handle_port_attr_set(dev, port_attr_info, check_cb,
8841cb33af1SFlorian Fainelli 					       set_cb);
8851cb33af1SFlorian Fainelli 	if (err == -EOPNOTSUPP)
8861cb33af1SFlorian Fainelli 		err = 0;
8871cb33af1SFlorian Fainelli 	return err;
8881cb33af1SFlorian Fainelli }
8891cb33af1SFlorian Fainelli EXPORT_SYMBOL_GPL(switchdev_handle_port_attr_set);
890957e2235SVladimir Oltean 
891957e2235SVladimir Oltean int switchdev_bridge_port_offload(struct net_device *brport_dev,
892957e2235SVladimir Oltean 				  struct net_device *dev, const void *ctx,
893957e2235SVladimir Oltean 				  struct notifier_block *atomic_nb,
894957e2235SVladimir Oltean 				  struct notifier_block *blocking_nb,
895957e2235SVladimir Oltean 				  bool tx_fwd_offload,
896957e2235SVladimir Oltean 				  struct netlink_ext_ack *extack)
897957e2235SVladimir Oltean {
898957e2235SVladimir Oltean 	struct switchdev_notifier_brport_info brport_info = {
899957e2235SVladimir Oltean 		.brport = {
900957e2235SVladimir Oltean 			.dev = dev,
901957e2235SVladimir Oltean 			.ctx = ctx,
902957e2235SVladimir Oltean 			.atomic_nb = atomic_nb,
903957e2235SVladimir Oltean 			.blocking_nb = blocking_nb,
904957e2235SVladimir Oltean 			.tx_fwd_offload = tx_fwd_offload,
905957e2235SVladimir Oltean 		},
906957e2235SVladimir Oltean 	};
907957e2235SVladimir Oltean 	int err;
908957e2235SVladimir Oltean 
909957e2235SVladimir Oltean 	ASSERT_RTNL();
910957e2235SVladimir Oltean 
911957e2235SVladimir Oltean 	err = call_switchdev_blocking_notifiers(SWITCHDEV_BRPORT_OFFLOADED,
912957e2235SVladimir Oltean 						brport_dev, &brport_info.info,
913957e2235SVladimir Oltean 						extack);
914957e2235SVladimir Oltean 	return notifier_to_errno(err);
915957e2235SVladimir Oltean }
916957e2235SVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_bridge_port_offload);
917957e2235SVladimir Oltean 
918957e2235SVladimir Oltean void switchdev_bridge_port_unoffload(struct net_device *brport_dev,
919957e2235SVladimir Oltean 				     const void *ctx,
920957e2235SVladimir Oltean 				     struct notifier_block *atomic_nb,
921957e2235SVladimir Oltean 				     struct notifier_block *blocking_nb)
922957e2235SVladimir Oltean {
923957e2235SVladimir Oltean 	struct switchdev_notifier_brport_info brport_info = {
924957e2235SVladimir Oltean 		.brport = {
925957e2235SVladimir Oltean 			.ctx = ctx,
926957e2235SVladimir Oltean 			.atomic_nb = atomic_nb,
927957e2235SVladimir Oltean 			.blocking_nb = blocking_nb,
928957e2235SVladimir Oltean 		},
929957e2235SVladimir Oltean 	};
930957e2235SVladimir Oltean 
931957e2235SVladimir Oltean 	ASSERT_RTNL();
932957e2235SVladimir Oltean 
933957e2235SVladimir Oltean 	call_switchdev_blocking_notifiers(SWITCHDEV_BRPORT_UNOFFLOADED,
934957e2235SVladimir Oltean 					  brport_dev, &brport_info.info,
935957e2235SVladimir Oltean 					  NULL);
936957e2235SVladimir Oltean }
937957e2235SVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_bridge_port_unoffload);
938f2e2857bSPetr Machata 
939f2e2857bSPetr Machata int switchdev_bridge_port_replay(struct net_device *brport_dev,
940f2e2857bSPetr Machata 				 struct net_device *dev, const void *ctx,
941f2e2857bSPetr Machata 				 struct notifier_block *atomic_nb,
942f2e2857bSPetr Machata 				 struct notifier_block *blocking_nb,
943f2e2857bSPetr Machata 				 struct netlink_ext_ack *extack)
944f2e2857bSPetr Machata {
945f2e2857bSPetr Machata 	struct switchdev_notifier_brport_info brport_info = {
946f2e2857bSPetr Machata 		.brport = {
947f2e2857bSPetr Machata 			.dev = dev,
948f2e2857bSPetr Machata 			.ctx = ctx,
949f2e2857bSPetr Machata 			.atomic_nb = atomic_nb,
950f2e2857bSPetr Machata 			.blocking_nb = blocking_nb,
951f2e2857bSPetr Machata 		},
952f2e2857bSPetr Machata 	};
953f2e2857bSPetr Machata 	int err;
954f2e2857bSPetr Machata 
955f2e2857bSPetr Machata 	ASSERT_RTNL();
956f2e2857bSPetr Machata 
957f2e2857bSPetr Machata 	err = call_switchdev_blocking_notifiers(SWITCHDEV_BRPORT_REPLAY,
958f2e2857bSPetr Machata 						brport_dev, &brport_info.info,
959f2e2857bSPetr Machata 						extack);
960f2e2857bSPetr Machata 	return notifier_to_errno(err);
961f2e2857bSPetr Machata }
962f2e2857bSPetr Machata EXPORT_SYMBOL_GPL(switchdev_bridge_port_replay);
963