xref: /openbmc/linux/net/switchdev/switchdev.c (revision 36db6e8484ed455bbb320d89a119378897ae991c)
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 <jiri@resnulli.us>
5f8f21471SScott Feldman  * Copyright (c) 2014-2015 Scott Feldman <sfeldma@gmail.com>
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 
switchdev_obj_eq(const struct switchdev_obj * a,const struct switchdev_obj * b)22603be954STobias Waldekranz static bool switchdev_obj_eq(const struct switchdev_obj *a,
23603be954STobias Waldekranz 			     const struct switchdev_obj *b)
24603be954STobias Waldekranz {
25603be954STobias Waldekranz 	const struct switchdev_obj_port_vlan *va, *vb;
26603be954STobias Waldekranz 	const struct switchdev_obj_port_mdb *ma, *mb;
27603be954STobias Waldekranz 
28603be954STobias Waldekranz 	if (a->id != b->id || a->orig_dev != b->orig_dev)
29603be954STobias Waldekranz 		return false;
30603be954STobias Waldekranz 
31603be954STobias Waldekranz 	switch (a->id) {
32603be954STobias Waldekranz 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
33603be954STobias Waldekranz 		va = SWITCHDEV_OBJ_PORT_VLAN(a);
34603be954STobias Waldekranz 		vb = SWITCHDEV_OBJ_PORT_VLAN(b);
35603be954STobias Waldekranz 		return va->flags == vb->flags &&
36603be954STobias Waldekranz 			va->vid == vb->vid &&
37603be954STobias Waldekranz 			va->changed == vb->changed;
38603be954STobias Waldekranz 	case SWITCHDEV_OBJ_ID_PORT_MDB:
39603be954STobias Waldekranz 	case SWITCHDEV_OBJ_ID_HOST_MDB:
40603be954STobias Waldekranz 		ma = SWITCHDEV_OBJ_PORT_MDB(a);
41603be954STobias Waldekranz 		mb = SWITCHDEV_OBJ_PORT_MDB(b);
42603be954STobias Waldekranz 		return ma->vid == mb->vid &&
43603be954STobias Waldekranz 			ether_addr_equal(ma->addr, mb->addr);
44603be954STobias Waldekranz 	default:
45603be954STobias Waldekranz 		break;
46603be954STobias Waldekranz 	}
47603be954STobias Waldekranz 
48603be954STobias Waldekranz 	BUG();
49603be954STobias Waldekranz }
50603be954STobias 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 
switchdev_deferred_dequeue(void)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  */
switchdev_deferred_process(void)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 
switchdev_deferred_process_work(struct work_struct * work)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 
switchdev_deferred_enqueue(struct net_device * dev,const void * data,size_t data_len,switchdev_deferred_func_t * func)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 
switchdev_port_attr_notify(enum switchdev_notifier_type nt,struct net_device * dev,const struct switchdev_attr * attr,struct netlink_ext_ack * extack)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 
switchdev_port_attr_set_now(struct net_device * dev,const struct switchdev_attr * attr,struct netlink_ext_ack * extack)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 
switchdev_port_attr_set_deferred(struct net_device * dev,const void * data)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 
switchdev_port_attr_set_defer(struct net_device * dev,const struct switchdev_attr * attr)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  */
switchdev_port_attr_set(struct net_device * dev,const struct switchdev_attr * attr,struct netlink_ext_ack * extack)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 
switchdev_obj_size(const struct switchdev_obj * obj)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 
switchdev_port_obj_notify(enum switchdev_notifier_type nt,struct net_device * dev,const struct switchdev_obj * obj,struct netlink_ext_ack * extack)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 
switchdev_port_obj_add_deferred(struct net_device * dev,const void * data)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 
switchdev_port_obj_add_defer(struct net_device * dev,const struct switchdev_obj * obj)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  */
switchdev_port_obj_add(struct net_device * dev,const struct switchdev_obj * obj,struct netlink_ext_ack * extack)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 
switchdev_port_obj_del_now(struct net_device * dev,const struct switchdev_obj * obj)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 
switchdev_port_obj_del_deferred(struct net_device * dev,const void * data)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 
switchdev_port_obj_del_defer(struct net_device * dev,const struct switchdev_obj * obj)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  */
switchdev_port_obj_del(struct net_device * dev,const struct switchdev_obj * obj)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 
339603be954STobias Waldekranz /**
340603be954STobias Waldekranz  *	switchdev_port_obj_act_is_deferred - Is object action pending?
341603be954STobias Waldekranz  *
342603be954STobias Waldekranz  *	@dev: port device
343603be954STobias Waldekranz  *	@nt: type of action; add or delete
344603be954STobias Waldekranz  *	@obj: object to test
345603be954STobias Waldekranz  *
346603be954STobias Waldekranz  *	Returns true if a deferred item is pending, which is
347603be954STobias Waldekranz  *	equivalent to the action @nt on an object @obj.
348603be954STobias Waldekranz  *
349603be954STobias Waldekranz  *	rtnl_lock must be held.
350603be954STobias Waldekranz  */
switchdev_port_obj_act_is_deferred(struct net_device * dev,enum switchdev_notifier_type nt,const struct switchdev_obj * obj)351603be954STobias Waldekranz bool switchdev_port_obj_act_is_deferred(struct net_device *dev,
352603be954STobias Waldekranz 					enum switchdev_notifier_type nt,
353603be954STobias Waldekranz 					const struct switchdev_obj *obj)
354603be954STobias Waldekranz {
355603be954STobias Waldekranz 	struct switchdev_deferred_item *dfitem;
356603be954STobias Waldekranz 	bool found = false;
357603be954STobias Waldekranz 
358603be954STobias Waldekranz 	ASSERT_RTNL();
359603be954STobias Waldekranz 
360603be954STobias Waldekranz 	spin_lock_bh(&deferred_lock);
361603be954STobias Waldekranz 
362603be954STobias Waldekranz 	list_for_each_entry(dfitem, &deferred, list) {
363603be954STobias Waldekranz 		if (dfitem->dev != dev)
364603be954STobias Waldekranz 			continue;
365603be954STobias Waldekranz 
366603be954STobias Waldekranz 		if ((dfitem->func == switchdev_port_obj_add_deferred &&
367603be954STobias Waldekranz 		     nt == SWITCHDEV_PORT_OBJ_ADD) ||
368603be954STobias Waldekranz 		    (dfitem->func == switchdev_port_obj_del_deferred &&
369603be954STobias Waldekranz 		     nt == SWITCHDEV_PORT_OBJ_DEL)) {
370603be954STobias Waldekranz 			if (switchdev_obj_eq((const void *)dfitem->data, obj)) {
371603be954STobias Waldekranz 				found = true;
372603be954STobias Waldekranz 				break;
373603be954STobias Waldekranz 			}
374603be954STobias Waldekranz 		}
375603be954STobias Waldekranz 	}
376603be954STobias Waldekranz 
377603be954STobias Waldekranz 	spin_unlock_bh(&deferred_lock);
378603be954STobias Waldekranz 
379603be954STobias Waldekranz 	return found;
380603be954STobias Waldekranz }
381603be954STobias Waldekranz EXPORT_SYMBOL_GPL(switchdev_port_obj_act_is_deferred);
382603be954STobias Waldekranz 
383ff5cf100SArkadi Sharshevsky static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain);
384*1f7d0518SAmit Cohen static RAW_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  */
register_switchdev_notifier(struct notifier_block * nb)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  */
unregister_switchdev_notifier(struct notifier_block * nb)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  */
call_switchdev_notifiers(unsigned long val,struct net_device * dev,struct switchdev_notifier_info * info,struct netlink_ext_ack * extack)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 
register_switchdev_blocking_notifier(struct notifier_block * nb)428a93e3b17SPetr Machata int register_switchdev_blocking_notifier(struct notifier_block *nb)
429a93e3b17SPetr Machata {
430*1f7d0518SAmit Cohen 	struct raw_notifier_head *chain = &switchdev_blocking_notif_chain;
431*1f7d0518SAmit Cohen 	int err;
432a93e3b17SPetr Machata 
433*1f7d0518SAmit Cohen 	rtnl_lock();
434*1f7d0518SAmit Cohen 	err = raw_notifier_chain_register(chain, nb);
435*1f7d0518SAmit Cohen 	rtnl_unlock();
436*1f7d0518SAmit Cohen 
437*1f7d0518SAmit Cohen 	return err;
438a93e3b17SPetr Machata }
439a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(register_switchdev_blocking_notifier);
440a93e3b17SPetr Machata 
441a93e3b17SPetr Machata int unregister_switchdev_blocking_notifier(struct notifier_block *nb)
442a93e3b17SPetr Machata {
443*1f7d0518SAmit Cohen 	struct raw_notifier_head *chain = &switchdev_blocking_notif_chain;
call_switchdev_blocking_notifiers(unsigned long val,struct net_device * dev,struct switchdev_notifier_info * info,struct netlink_ext_ack * extack)444*1f7d0518SAmit Cohen 	int err;
445a93e3b17SPetr Machata 
446*1f7d0518SAmit Cohen 	rtnl_lock();
447*1f7d0518SAmit Cohen 	err = raw_notifier_chain_unregister(chain, nb);
448*1f7d0518SAmit Cohen 	rtnl_unlock();
449*1f7d0518SAmit Cohen 
450*1f7d0518SAmit Cohen 	return err;
451a93e3b17SPetr Machata }
452a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(unregister_switchdev_blocking_notifier);
453a93e3b17SPetr Machata 
454a93e3b17SPetr Machata int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev,
455479c86dcSPetr Machata 				      struct switchdev_notifier_info *info,
456479c86dcSPetr Machata 				      struct netlink_ext_ack *extack)
457a93e3b17SPetr Machata {
458*1f7d0518SAmit Cohen 	ASSERT_RTNL();
459a93e3b17SPetr Machata 	info->dev = dev;
460479c86dcSPetr Machata 	info->extack = extack;
461*1f7d0518SAmit Cohen 	return raw_notifier_call_chain(&switchdev_blocking_notif_chain,
462a93e3b17SPetr Machata 				       val, info);
switchdev_lower_dev_walk(struct net_device * lower_dev,struct netdev_nested_priv * priv)463a93e3b17SPetr Machata }
464a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(call_switchdev_blocking_notifiers);
465a93e3b17SPetr Machata 
4662b0a5688SVladimir Oltean struct switchdev_nested_priv {
4672b0a5688SVladimir Oltean 	bool (*check_cb)(const struct net_device *dev);
4682b0a5688SVladimir Oltean 	bool (*foreign_dev_check_cb)(const struct net_device *dev,
4692b0a5688SVladimir Oltean 				     const struct net_device *foreign_dev);
4702b0a5688SVladimir Oltean 	const struct net_device *dev;
4712b0a5688SVladimir Oltean 	struct net_device *lower_dev;
4722b0a5688SVladimir Oltean };
4732b0a5688SVladimir Oltean 
4742b0a5688SVladimir Oltean static int switchdev_lower_dev_walk(struct net_device *lower_dev,
4752b0a5688SVladimir Oltean 				    struct netdev_nested_priv *priv)
4762b0a5688SVladimir Oltean {
4772b0a5688SVladimir Oltean 	struct switchdev_nested_priv *switchdev_priv = priv->data;
4782b0a5688SVladimir Oltean 	bool (*foreign_dev_check_cb)(const struct net_device *dev,
4792b0a5688SVladimir Oltean 				     const struct net_device *foreign_dev);
4802b0a5688SVladimir Oltean 	bool (*check_cb)(const struct net_device *dev);
4812b0a5688SVladimir Oltean 	const struct net_device *dev;
4822b0a5688SVladimir Oltean 
4832b0a5688SVladimir Oltean 	check_cb = switchdev_priv->check_cb;
4842b0a5688SVladimir Oltean 	foreign_dev_check_cb = switchdev_priv->foreign_dev_check_cb;
switchdev_lower_dev_find_rcu(struct net_device * dev,bool (* check_cb)(const struct net_device * dev),bool (* foreign_dev_check_cb)(const struct net_device * dev,const struct net_device * foreign_dev))4852b0a5688SVladimir Oltean 	dev = switchdev_priv->dev;
4862b0a5688SVladimir Oltean 
4872b0a5688SVladimir Oltean 	if (check_cb(lower_dev) && !foreign_dev_check_cb(lower_dev, dev)) {
4882b0a5688SVladimir Oltean 		switchdev_priv->lower_dev = lower_dev;
4892b0a5688SVladimir Oltean 		return 1;
4902b0a5688SVladimir Oltean 	}
4912b0a5688SVladimir Oltean 
4922b0a5688SVladimir Oltean 	return 0;
4932b0a5688SVladimir Oltean }
4942b0a5688SVladimir Oltean 
4952b0a5688SVladimir Oltean static struct net_device *
4967b465f4cSVladimir Oltean switchdev_lower_dev_find_rcu(struct net_device *dev,
4972b0a5688SVladimir Oltean 			     bool (*check_cb)(const struct net_device *dev),
4982b0a5688SVladimir Oltean 			     bool (*foreign_dev_check_cb)(const struct net_device *dev,
4992b0a5688SVladimir Oltean 							  const struct net_device *foreign_dev))
5002b0a5688SVladimir Oltean {
5012b0a5688SVladimir Oltean 	struct switchdev_nested_priv switchdev_priv = {
5022b0a5688SVladimir Oltean 		.check_cb = check_cb,
5032b0a5688SVladimir Oltean 		.foreign_dev_check_cb = foreign_dev_check_cb,
5042b0a5688SVladimir Oltean 		.dev = dev,
5052b0a5688SVladimir Oltean 		.lower_dev = NULL,
switchdev_lower_dev_find(struct net_device * dev,bool (* check_cb)(const struct net_device * dev),bool (* foreign_dev_check_cb)(const struct net_device * dev,const struct net_device * foreign_dev))5062b0a5688SVladimir Oltean 	};
5072b0a5688SVladimir Oltean 	struct netdev_nested_priv priv = {
5082b0a5688SVladimir Oltean 		.data = &switchdev_priv,
5092b0a5688SVladimir Oltean 	};
5102b0a5688SVladimir Oltean 
5112b0a5688SVladimir Oltean 	netdev_walk_all_lower_dev_rcu(dev, switchdev_lower_dev_walk, &priv);
5122b0a5688SVladimir Oltean 
5132b0a5688SVladimir Oltean 	return switchdev_priv.lower_dev;
5142b0a5688SVladimir Oltean }
5152b0a5688SVladimir Oltean 
516c4076cddSVladimir Oltean static struct net_device *
517c4076cddSVladimir Oltean switchdev_lower_dev_find(struct net_device *dev,
518c4076cddSVladimir Oltean 			 bool (*check_cb)(const struct net_device *dev),
519c4076cddSVladimir Oltean 			 bool (*foreign_dev_check_cb)(const struct net_device *dev,
520c4076cddSVladimir Oltean 						      const struct net_device *foreign_dev))
521c4076cddSVladimir Oltean {
522c4076cddSVladimir Oltean 	struct switchdev_nested_priv switchdev_priv = {
523c4076cddSVladimir Oltean 		.check_cb = check_cb,
524c4076cddSVladimir Oltean 		.foreign_dev_check_cb = foreign_dev_check_cb,
525c4076cddSVladimir Oltean 		.dev = dev,
__switchdev_handle_fdb_event_to_device(struct net_device * dev,struct net_device * orig_dev,unsigned long event,const struct switchdev_notifier_fdb_info * fdb_info,bool (* check_cb)(const struct net_device * dev),bool (* foreign_dev_check_cb)(const struct net_device * dev,const struct net_device * foreign_dev),int (* mod_cb)(struct net_device * dev,struct net_device * orig_dev,unsigned long event,const void * ctx,const struct switchdev_notifier_fdb_info * fdb_info))526c4076cddSVladimir Oltean 		.lower_dev = NULL,
527c4076cddSVladimir Oltean 	};
528c4076cddSVladimir Oltean 	struct netdev_nested_priv priv = {
529c4076cddSVladimir Oltean 		.data = &switchdev_priv,
530c4076cddSVladimir Oltean 	};
531c4076cddSVladimir Oltean 
532c4076cddSVladimir Oltean 	netdev_walk_all_lower_dev(dev, switchdev_lower_dev_walk, &priv);
533c4076cddSVladimir Oltean 
534c4076cddSVladimir Oltean 	return switchdev_priv.lower_dev;
535c4076cddSVladimir Oltean }
536c4076cddSVladimir Oltean 
537716a30a9SVladimir Oltean static int __switchdev_handle_fdb_event_to_device(struct net_device *dev,
538716a30a9SVladimir Oltean 		struct net_device *orig_dev, unsigned long event,
5398ca07176SVladimir Oltean 		const struct switchdev_notifier_fdb_info *fdb_info,
5408ca07176SVladimir Oltean 		bool (*check_cb)(const struct net_device *dev),
5418ca07176SVladimir Oltean 		bool (*foreign_dev_check_cb)(const struct net_device *dev,
5428ca07176SVladimir Oltean 					     const struct net_device *foreign_dev),
543716a30a9SVladimir Oltean 		int (*mod_cb)(struct net_device *dev, struct net_device *orig_dev,
544716a30a9SVladimir Oltean 			      unsigned long event, const void *ctx,
5458ca07176SVladimir Oltean 			      const struct switchdev_notifier_fdb_info *fdb_info))
5468ca07176SVladimir Oltean {
5478ca07176SVladimir Oltean 	const struct switchdev_notifier_info *info = &fdb_info->info;
548ec638740SVladimir Oltean 	struct net_device *br, *lower_dev, *switchdev;
5498ca07176SVladimir Oltean 	struct list_head *iter;
5508ca07176SVladimir Oltean 	int err = -EOPNOTSUPP;
5518ca07176SVladimir Oltean 
5522b0a5688SVladimir Oltean 	if (check_cb(dev))
553716a30a9SVladimir Oltean 		return mod_cb(dev, orig_dev, event, info->ctx, fdb_info);
5548ca07176SVladimir Oltean 
5558ca07176SVladimir Oltean 	/* Recurse through lower interfaces in case the FDB entry is pointing
556ec638740SVladimir Oltean 	 * towards a bridge or a LAG device.
5578ca07176SVladimir Oltean 	 */
5588ca07176SVladimir Oltean 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
5598ca07176SVladimir Oltean 		/* Do not propagate FDB entries across bridges */
5608ca07176SVladimir Oltean 		if (netif_is_bridge_master(lower_dev))
5618ca07176SVladimir Oltean 			continue;
5628ca07176SVladimir Oltean 
5632b0a5688SVladimir Oltean 		/* Bridge ports might be either us, or LAG interfaces
5642b0a5688SVladimir Oltean 		 * that we offload.
5652b0a5688SVladimir Oltean 		 */
5662b0a5688SVladimir Oltean 		if (!check_cb(lower_dev) &&
5677b465f4cSVladimir Oltean 		    !switchdev_lower_dev_find_rcu(lower_dev, check_cb,
5682b0a5688SVladimir Oltean 						  foreign_dev_check_cb))
5692b0a5688SVladimir Oltean 			continue;
5702b0a5688SVladimir Oltean 
571716a30a9SVladimir Oltean 		err = __switchdev_handle_fdb_event_to_device(lower_dev, orig_dev,
572716a30a9SVladimir Oltean 							     event, fdb_info, check_cb,
5738ca07176SVladimir Oltean 							     foreign_dev_check_cb,
574ec638740SVladimir Oltean 							     mod_cb);
5758ca07176SVladimir Oltean 		if (err && err != -EOPNOTSUPP)
5768ca07176SVladimir Oltean 			return err;
5778ca07176SVladimir Oltean 	}
5788ca07176SVladimir Oltean 
5792b0a5688SVladimir Oltean 	/* Event is neither on a bridge nor a LAG. Check whether it is on an
5802b0a5688SVladimir Oltean 	 * interface that is in a bridge with us.
5812b0a5688SVladimir Oltean 	 */
5822b0a5688SVladimir Oltean 	br = netdev_master_upper_dev_get_rcu(dev);
5832b0a5688SVladimir Oltean 	if (!br || !netif_is_bridge_master(br))
5842b0a5688SVladimir Oltean 		return 0;
5852b0a5688SVladimir Oltean 
586ec638740SVladimir Oltean 	switchdev = switchdev_lower_dev_find_rcu(br, check_cb, foreign_dev_check_cb);
switchdev_handle_fdb_event_to_device(struct net_device * dev,unsigned long event,const struct switchdev_notifier_fdb_info * fdb_info,bool (* check_cb)(const struct net_device * dev),bool (* foreign_dev_check_cb)(const struct net_device * dev,const struct net_device * foreign_dev),int (* mod_cb)(struct net_device * dev,struct net_device * orig_dev,unsigned long event,const void * ctx,const struct switchdev_notifier_fdb_info * fdb_info))587ec638740SVladimir Oltean 	if (!switchdev)
5882b0a5688SVladimir Oltean 		return 0;
5892b0a5688SVladimir Oltean 
590ec638740SVladimir Oltean 	if (!foreign_dev_check_cb(switchdev, dev))
591ec638740SVladimir Oltean 		return err;
592ec638740SVladimir Oltean 
593716a30a9SVladimir Oltean 	return __switchdev_handle_fdb_event_to_device(br, orig_dev, event, fdb_info,
5942b0a5688SVladimir Oltean 						      check_cb, foreign_dev_check_cb,
595ec638740SVladimir Oltean 						      mod_cb);
5968ca07176SVladimir Oltean }
5978ca07176SVladimir Oltean 
598716a30a9SVladimir Oltean int switchdev_handle_fdb_event_to_device(struct net_device *dev, unsigned long event,
5998ca07176SVladimir Oltean 		const struct switchdev_notifier_fdb_info *fdb_info,
6008ca07176SVladimir Oltean 		bool (*check_cb)(const struct net_device *dev),
6018ca07176SVladimir Oltean 		bool (*foreign_dev_check_cb)(const struct net_device *dev,
6028ca07176SVladimir Oltean 					     const struct net_device *foreign_dev),
603716a30a9SVladimir Oltean 		int (*mod_cb)(struct net_device *dev, struct net_device *orig_dev,
604716a30a9SVladimir Oltean 			      unsigned long event, const void *ctx,
6058ca07176SVladimir Oltean 			      const struct switchdev_notifier_fdb_info *fdb_info))
6068ca07176SVladimir Oltean {
6078ca07176SVladimir Oltean 	int err;
__switchdev_handle_port_obj_add(struct net_device * dev,struct switchdev_notifier_port_obj_info * port_obj_info,bool (* check_cb)(const struct net_device * dev),bool (* foreign_dev_check_cb)(const struct net_device * dev,const struct net_device * foreign_dev),int (* add_cb)(struct net_device * dev,const void * ctx,const struct switchdev_obj * obj,struct netlink_ext_ack * extack))6088ca07176SVladimir Oltean 
609716a30a9SVladimir Oltean 	err = __switchdev_handle_fdb_event_to_device(dev, dev, event, fdb_info,
610716a30a9SVladimir Oltean 						     check_cb, foreign_dev_check_cb,
611ec638740SVladimir Oltean 						     mod_cb);
6128ca07176SVladimir Oltean 	if (err == -EOPNOTSUPP)
6138ca07176SVladimir Oltean 		err = 0;
6148ca07176SVladimir Oltean 
6158ca07176SVladimir Oltean 	return err;
6168ca07176SVladimir Oltean }
617716a30a9SVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_handle_fdb_event_to_device);
6188ca07176SVladimir Oltean 
619f30f0601SPetr Machata static int __switchdev_handle_port_obj_add(struct net_device *dev,
620f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
621f30f0601SPetr Machata 			bool (*check_cb)(const struct net_device *dev),
622c4076cddSVladimir Oltean 			bool (*foreign_dev_check_cb)(const struct net_device *dev,
623c4076cddSVladimir Oltean 						     const struct net_device *foreign_dev),
62469bfac96SVladimir Oltean 			int (*add_cb)(struct net_device *dev, const void *ctx,
625f30f0601SPetr Machata 				      const struct switchdev_obj *obj,
62669213513SPetr Machata 				      struct netlink_ext_ack *extack))
627f30f0601SPetr Machata {
62869bfac96SVladimir Oltean 	struct switchdev_notifier_info *info = &port_obj_info->info;
629acd8df58SVladimir Oltean 	struct net_device *br, *lower_dev, *switchdev;
63069213513SPetr Machata 	struct netlink_ext_ack *extack;
631f30f0601SPetr Machata 	struct list_head *iter;
632f30f0601SPetr Machata 	int err = -EOPNOTSUPP;
633f30f0601SPetr Machata 
63469bfac96SVladimir Oltean 	extack = switchdev_notifier_info_to_extack(info);
63569213513SPetr Machata 
636f30f0601SPetr Machata 	if (check_cb(dev)) {
63769bfac96SVladimir Oltean 		err = add_cb(dev, info->ctx, port_obj_info->obj, extack);
63820776b46SRasmus Villemoes 		if (err != -EOPNOTSUPP)
639f30f0601SPetr Machata 			port_obj_info->handled = true;
64020776b46SRasmus Villemoes 		return err;
641f30f0601SPetr Machata 	}
642f30f0601SPetr Machata 
643f30f0601SPetr Machata 	/* Switch ports might be stacked under e.g. a LAG. Ignore the
644f30f0601SPetr Machata 	 * unsupported devices, another driver might be able to handle them. But
645f30f0601SPetr Machata 	 * propagate to the callers any hard errors.
646f30f0601SPetr Machata 	 *
647f30f0601SPetr Machata 	 * If the driver does its own bookkeeping of stacked ports, it's not
648f30f0601SPetr Machata 	 * necessary to go through this helper.
649f30f0601SPetr Machata 	 */
650f30f0601SPetr Machata 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
65107c6f980SRussell King 		if (netif_is_bridge_master(lower_dev))
65207c6f980SRussell King 			continue;
65307c6f980SRussell King 
654c4076cddSVladimir Oltean 		/* When searching for switchdev interfaces that are neighbors
655c4076cddSVladimir Oltean 		 * of foreign ones, and @dev is a bridge, do not recurse on the
656c4076cddSVladimir Oltean 		 * foreign interface again, it was already visited.
657c4076cddSVladimir Oltean 		 */
658c4076cddSVladimir Oltean 		if (foreign_dev_check_cb && !check_cb(lower_dev) &&
659c4076cddSVladimir Oltean 		    !switchdev_lower_dev_find(lower_dev, check_cb, foreign_dev_check_cb))
660c4076cddSVladimir Oltean 			continue;
661c4076cddSVladimir Oltean 
662f30f0601SPetr Machata 		err = __switchdev_handle_port_obj_add(lower_dev, port_obj_info,
663c4076cddSVladimir Oltean 						      check_cb, foreign_dev_check_cb,
664c4076cddSVladimir Oltean 						      add_cb);
665f30f0601SPetr Machata 		if (err && err != -EOPNOTSUPP)
666f30f0601SPetr Machata 			return err;
667f30f0601SPetr Machata 	}
668f30f0601SPetr Machata 
669c4076cddSVladimir Oltean 	/* Event is neither on a bridge nor a LAG. Check whether it is on an
670c4076cddSVladimir Oltean 	 * interface that is in a bridge with us.
671c4076cddSVladimir Oltean 	 */
672c4076cddSVladimir Oltean 	if (!foreign_dev_check_cb)
673f30f0601SPetr Machata 		return err;
674c4076cddSVladimir Oltean 
675c4076cddSVladimir Oltean 	br = netdev_master_upper_dev_get(dev);
676c4076cddSVladimir Oltean 	if (!br || !netif_is_bridge_master(br))
677c4076cddSVladimir Oltean 		return err;
678c4076cddSVladimir Oltean 
679acd8df58SVladimir Oltean 	switchdev = switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb);
680acd8df58SVladimir Oltean 	if (!switchdev)
681acd8df58SVladimir Oltean 		return err;
682acd8df58SVladimir Oltean 
switchdev_handle_port_obj_add(struct net_device * dev,struct switchdev_notifier_port_obj_info * port_obj_info,bool (* check_cb)(const struct net_device * dev),int (* add_cb)(struct net_device * dev,const void * ctx,const struct switchdev_obj * obj,struct netlink_ext_ack * extack))683acd8df58SVladimir Oltean 	if (!foreign_dev_check_cb(switchdev, dev))
684c4076cddSVladimir Oltean 		return err;
685c4076cddSVladimir Oltean 
686c4076cddSVladimir Oltean 	return __switchdev_handle_port_obj_add(br, port_obj_info, check_cb,
687c4076cddSVladimir Oltean 					       foreign_dev_check_cb, add_cb);
688f30f0601SPetr Machata }
689f30f0601SPetr Machata 
690c4076cddSVladimir Oltean /* Pass through a port object addition, if @dev passes @check_cb, or replicate
691c4076cddSVladimir Oltean  * it towards all lower interfaces of @dev that pass @check_cb, if @dev is a
692c4076cddSVladimir Oltean  * bridge or a LAG.
693c4076cddSVladimir Oltean  */
694f30f0601SPetr Machata int switchdev_handle_port_obj_add(struct net_device *dev,
695f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
696f30f0601SPetr Machata 			bool (*check_cb)(const struct net_device *dev),
69769bfac96SVladimir Oltean 			int (*add_cb)(struct net_device *dev, const void *ctx,
698f30f0601SPetr Machata 				      const struct switchdev_obj *obj,
69969213513SPetr Machata 				      struct netlink_ext_ack *extack))
700f30f0601SPetr Machata {
701f30f0601SPetr Machata 	int err;
702f30f0601SPetr Machata 
703f30f0601SPetr Machata 	err = __switchdev_handle_port_obj_add(dev, port_obj_info, check_cb,
switchdev_handle_port_obj_add_foreign(struct net_device * dev,struct switchdev_notifier_port_obj_info * port_obj_info,bool (* check_cb)(const struct net_device * dev),bool (* foreign_dev_check_cb)(const struct net_device * dev,const struct net_device * foreign_dev),int (* add_cb)(struct net_device * dev,const void * ctx,const struct switchdev_obj * obj,struct netlink_ext_ack * extack))704c4076cddSVladimir Oltean 					      NULL, add_cb);
705f30f0601SPetr Machata 	if (err == -EOPNOTSUPP)
706f30f0601SPetr Machata 		err = 0;
707f30f0601SPetr Machata 	return err;
708f30f0601SPetr Machata }
709f30f0601SPetr Machata EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add);
710f30f0601SPetr Machata 
711c4076cddSVladimir Oltean /* Same as switchdev_handle_port_obj_add(), except if object is notified on a
712c4076cddSVladimir Oltean  * @dev that passes @foreign_dev_check_cb, it is replicated towards all devices
713c4076cddSVladimir Oltean  * that pass @check_cb and are in the same bridge as @dev.
714c4076cddSVladimir Oltean  */
715c4076cddSVladimir Oltean int switchdev_handle_port_obj_add_foreign(struct net_device *dev,
716c4076cddSVladimir Oltean 			struct switchdev_notifier_port_obj_info *port_obj_info,
717c4076cddSVladimir Oltean 			bool (*check_cb)(const struct net_device *dev),
718c4076cddSVladimir Oltean 			bool (*foreign_dev_check_cb)(const struct net_device *dev,
719c4076cddSVladimir Oltean 						     const struct net_device *foreign_dev),
720c4076cddSVladimir Oltean 			int (*add_cb)(struct net_device *dev, const void *ctx,
721c4076cddSVladimir Oltean 				      const struct switchdev_obj *obj,
722c4076cddSVladimir Oltean 				      struct netlink_ext_ack *extack))
__switchdev_handle_port_obj_del(struct net_device * dev,struct switchdev_notifier_port_obj_info * port_obj_info,bool (* check_cb)(const struct net_device * dev),bool (* foreign_dev_check_cb)(const struct net_device * dev,const struct net_device * foreign_dev),int (* del_cb)(struct net_device * dev,const void * ctx,const struct switchdev_obj * obj))723c4076cddSVladimir Oltean {
724c4076cddSVladimir Oltean 	int err;
725c4076cddSVladimir Oltean 
726c4076cddSVladimir Oltean 	err = __switchdev_handle_port_obj_add(dev, port_obj_info, check_cb,
727c4076cddSVladimir Oltean 					      foreign_dev_check_cb, add_cb);
728c4076cddSVladimir Oltean 	if (err == -EOPNOTSUPP)
729c4076cddSVladimir Oltean 		err = 0;
730c4076cddSVladimir Oltean 	return err;
731c4076cddSVladimir Oltean }
732c4076cddSVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add_foreign);
733c4076cddSVladimir Oltean 
734f30f0601SPetr Machata static int __switchdev_handle_port_obj_del(struct net_device *dev,
735f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
736f30f0601SPetr Machata 			bool (*check_cb)(const struct net_device *dev),
737c4076cddSVladimir Oltean 			bool (*foreign_dev_check_cb)(const struct net_device *dev,
738c4076cddSVladimir Oltean 						     const struct net_device *foreign_dev),
73969bfac96SVladimir Oltean 			int (*del_cb)(struct net_device *dev, const void *ctx,
740f30f0601SPetr Machata 				      const struct switchdev_obj *obj))
741f30f0601SPetr Machata {
74269bfac96SVladimir Oltean 	struct switchdev_notifier_info *info = &port_obj_info->info;
743acd8df58SVladimir Oltean 	struct net_device *br, *lower_dev, *switchdev;
744f30f0601SPetr Machata 	struct list_head *iter;
745f30f0601SPetr Machata 	int err = -EOPNOTSUPP;
746f30f0601SPetr Machata 
747f30f0601SPetr Machata 	if (check_cb(dev)) {
74869bfac96SVladimir Oltean 		err = del_cb(dev, info->ctx, port_obj_info->obj);
74920776b46SRasmus Villemoes 		if (err != -EOPNOTSUPP)
750f30f0601SPetr Machata 			port_obj_info->handled = true;
75120776b46SRasmus Villemoes 		return err;
752f30f0601SPetr Machata 	}
753f30f0601SPetr Machata 
754f30f0601SPetr Machata 	/* Switch ports might be stacked under e.g. a LAG. Ignore the
755f30f0601SPetr Machata 	 * unsupported devices, another driver might be able to handle them. But
756f30f0601SPetr Machata 	 * propagate to the callers any hard errors.
757f30f0601SPetr Machata 	 *
758f30f0601SPetr Machata 	 * If the driver does its own bookkeeping of stacked ports, it's not
759f30f0601SPetr Machata 	 * necessary to go through this helper.
760f30f0601SPetr Machata 	 */
761f30f0601SPetr Machata 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
76207c6f980SRussell King 		if (netif_is_bridge_master(lower_dev))
76307c6f980SRussell King 			continue;
76407c6f980SRussell King 
765c4076cddSVladimir Oltean 		/* When searching for switchdev interfaces that are neighbors
766c4076cddSVladimir Oltean 		 * of foreign ones, and @dev is a bridge, do not recurse on the
767c4076cddSVladimir Oltean 		 * foreign interface again, it was already visited.
768c4076cddSVladimir Oltean 		 */
769c4076cddSVladimir Oltean 		if (foreign_dev_check_cb && !check_cb(lower_dev) &&
770c4076cddSVladimir Oltean 		    !switchdev_lower_dev_find(lower_dev, check_cb, foreign_dev_check_cb))
771c4076cddSVladimir Oltean 			continue;
772c4076cddSVladimir Oltean 
773f30f0601SPetr Machata 		err = __switchdev_handle_port_obj_del(lower_dev, port_obj_info,
774c4076cddSVladimir Oltean 						      check_cb, foreign_dev_check_cb,
775c4076cddSVladimir Oltean 						      del_cb);
776f30f0601SPetr Machata 		if (err && err != -EOPNOTSUPP)
777f30f0601SPetr Machata 			return err;
778f30f0601SPetr Machata 	}
779f30f0601SPetr Machata 
780c4076cddSVladimir Oltean 	/* Event is neither on a bridge nor a LAG. Check whether it is on an
781c4076cddSVladimir Oltean 	 * interface that is in a bridge with us.
782c4076cddSVladimir Oltean 	 */
783c4076cddSVladimir Oltean 	if (!foreign_dev_check_cb)
784f30f0601SPetr Machata 		return err;
785c4076cddSVladimir Oltean 
786c4076cddSVladimir Oltean 	br = netdev_master_upper_dev_get(dev);
787c4076cddSVladimir Oltean 	if (!br || !netif_is_bridge_master(br))
788c4076cddSVladimir Oltean 		return err;
789c4076cddSVladimir Oltean 
790acd8df58SVladimir Oltean 	switchdev = switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb);
791acd8df58SVladimir Oltean 	if (!switchdev)
792acd8df58SVladimir Oltean 		return err;
793acd8df58SVladimir Oltean 
switchdev_handle_port_obj_del(struct net_device * dev,struct switchdev_notifier_port_obj_info * port_obj_info,bool (* check_cb)(const struct net_device * dev),int (* del_cb)(struct net_device * dev,const void * ctx,const struct switchdev_obj * obj))794acd8df58SVladimir Oltean 	if (!foreign_dev_check_cb(switchdev, dev))
795c4076cddSVladimir Oltean 		return err;
796c4076cddSVladimir Oltean 
797c4076cddSVladimir Oltean 	return __switchdev_handle_port_obj_del(br, port_obj_info, check_cb,
798c4076cddSVladimir Oltean 					       foreign_dev_check_cb, del_cb);
799f30f0601SPetr Machata }
800f30f0601SPetr Machata 
801c4076cddSVladimir Oltean /* Pass through a port object deletion, if @dev passes @check_cb, or replicate
802c4076cddSVladimir Oltean  * it towards all lower interfaces of @dev that pass @check_cb, if @dev is a
803c4076cddSVladimir Oltean  * bridge or a LAG.
804c4076cddSVladimir Oltean  */
805f30f0601SPetr Machata int switchdev_handle_port_obj_del(struct net_device *dev,
806f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
807f30f0601SPetr Machata 			bool (*check_cb)(const struct net_device *dev),
80869bfac96SVladimir Oltean 			int (*del_cb)(struct net_device *dev, const void *ctx,
809f30f0601SPetr Machata 				      const struct switchdev_obj *obj))
810f30f0601SPetr Machata {
811f30f0601SPetr Machata 	int err;
812f30f0601SPetr Machata 
813f30f0601SPetr Machata 	err = __switchdev_handle_port_obj_del(dev, port_obj_info, check_cb,
switchdev_handle_port_obj_del_foreign(struct net_device * dev,struct switchdev_notifier_port_obj_info * port_obj_info,bool (* check_cb)(const struct net_device * dev),bool (* foreign_dev_check_cb)(const struct net_device * dev,const struct net_device * foreign_dev),int (* del_cb)(struct net_device * dev,const void * ctx,const struct switchdev_obj * obj))814c4076cddSVladimir Oltean 					      NULL, del_cb);
815f30f0601SPetr Machata 	if (err == -EOPNOTSUPP)
816f30f0601SPetr Machata 		err = 0;
817f30f0601SPetr Machata 	return err;
818f30f0601SPetr Machata }
819f30f0601SPetr Machata EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del);
8201cb33af1SFlorian Fainelli 
821c4076cddSVladimir Oltean /* Same as switchdev_handle_port_obj_del(), except if object is notified on a
822c4076cddSVladimir Oltean  * @dev that passes @foreign_dev_check_cb, it is replicated towards all devices
823c4076cddSVladimir Oltean  * that pass @check_cb and are in the same bridge as @dev.
824c4076cddSVladimir Oltean  */
825c4076cddSVladimir Oltean int switchdev_handle_port_obj_del_foreign(struct net_device *dev,
826c4076cddSVladimir Oltean 			struct switchdev_notifier_port_obj_info *port_obj_info,
827c4076cddSVladimir Oltean 			bool (*check_cb)(const struct net_device *dev),
828c4076cddSVladimir Oltean 			bool (*foreign_dev_check_cb)(const struct net_device *dev,
829c4076cddSVladimir Oltean 						     const struct net_device *foreign_dev),
830c4076cddSVladimir Oltean 			int (*del_cb)(struct net_device *dev, const void *ctx,
831c4076cddSVladimir Oltean 				      const struct switchdev_obj *obj))
__switchdev_handle_port_attr_set(struct net_device * dev,struct switchdev_notifier_port_attr_info * port_attr_info,bool (* check_cb)(const struct net_device * dev),int (* set_cb)(struct net_device * dev,const void * ctx,const struct switchdev_attr * attr,struct netlink_ext_ack * extack))832c4076cddSVladimir Oltean {
833c4076cddSVladimir Oltean 	int err;
834c4076cddSVladimir Oltean 
835c4076cddSVladimir Oltean 	err = __switchdev_handle_port_obj_del(dev, port_obj_info, check_cb,
836c4076cddSVladimir Oltean 					      foreign_dev_check_cb, del_cb);
837c4076cddSVladimir Oltean 	if (err == -EOPNOTSUPP)
838c4076cddSVladimir Oltean 		err = 0;
839c4076cddSVladimir Oltean 	return err;
840c4076cddSVladimir Oltean }
841c4076cddSVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del_foreign);
842c4076cddSVladimir Oltean 
8431cb33af1SFlorian Fainelli static int __switchdev_handle_port_attr_set(struct net_device *dev,
8441cb33af1SFlorian Fainelli 			struct switchdev_notifier_port_attr_info *port_attr_info,
8451cb33af1SFlorian Fainelli 			bool (*check_cb)(const struct net_device *dev),
84669bfac96SVladimir Oltean 			int (*set_cb)(struct net_device *dev, const void *ctx,
8474c08c586SVladimir Oltean 				      const struct switchdev_attr *attr,
8484c08c586SVladimir Oltean 				      struct netlink_ext_ack *extack))
8491cb33af1SFlorian Fainelli {
85069bfac96SVladimir Oltean 	struct switchdev_notifier_info *info = &port_attr_info->info;
8514c08c586SVladimir Oltean 	struct netlink_ext_ack *extack;
8521cb33af1SFlorian Fainelli 	struct net_device *lower_dev;
8531cb33af1SFlorian Fainelli 	struct list_head *iter;
8541cb33af1SFlorian Fainelli 	int err = -EOPNOTSUPP;
8551cb33af1SFlorian Fainelli 
85669bfac96SVladimir Oltean 	extack = switchdev_notifier_info_to_extack(info);
8574c08c586SVladimir Oltean 
8581cb33af1SFlorian Fainelli 	if (check_cb(dev)) {
85969bfac96SVladimir Oltean 		err = set_cb(dev, info->ctx, port_attr_info->attr, extack);
86020776b46SRasmus Villemoes 		if (err != -EOPNOTSUPP)
8611cb33af1SFlorian Fainelli 			port_attr_info->handled = true;
86220776b46SRasmus Villemoes 		return err;
8631cb33af1SFlorian Fainelli 	}
8641cb33af1SFlorian Fainelli 
8651cb33af1SFlorian Fainelli 	/* Switch ports might be stacked under e.g. a LAG. Ignore the
8661cb33af1SFlorian Fainelli 	 * unsupported devices, another driver might be able to handle them. But
8671cb33af1SFlorian Fainelli 	 * propagate to the callers any hard errors.
8681cb33af1SFlorian Fainelli 	 *
8691cb33af1SFlorian Fainelli 	 * If the driver does its own bookkeeping of stacked ports, it's not
8701cb33af1SFlorian Fainelli 	 * necessary to go through this helper.
8711cb33af1SFlorian Fainelli 	 */
8721cb33af1SFlorian Fainelli 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
87307c6f980SRussell King 		if (netif_is_bridge_master(lower_dev))
switchdev_handle_port_attr_set(struct net_device * dev,struct switchdev_notifier_port_attr_info * port_attr_info,bool (* check_cb)(const struct net_device * dev),int (* set_cb)(struct net_device * dev,const void * ctx,const struct switchdev_attr * attr,struct netlink_ext_ack * extack))87407c6f980SRussell King 			continue;
87507c6f980SRussell King 
8761cb33af1SFlorian Fainelli 		err = __switchdev_handle_port_attr_set(lower_dev, port_attr_info,
8771cb33af1SFlorian Fainelli 						       check_cb, set_cb);
8781cb33af1SFlorian Fainelli 		if (err && err != -EOPNOTSUPP)
8791cb33af1SFlorian Fainelli 			return err;
8801cb33af1SFlorian Fainelli 	}
8811cb33af1SFlorian Fainelli 
8821cb33af1SFlorian Fainelli 	return err;
8831cb33af1SFlorian Fainelli }
8841cb33af1SFlorian Fainelli 
8851cb33af1SFlorian Fainelli int switchdev_handle_port_attr_set(struct net_device *dev,
8861cb33af1SFlorian Fainelli 			struct switchdev_notifier_port_attr_info *port_attr_info,
8871cb33af1SFlorian Fainelli 			bool (*check_cb)(const struct net_device *dev),
88869bfac96SVladimir Oltean 			int (*set_cb)(struct net_device *dev, const void *ctx,
8894c08c586SVladimir Oltean 				      const struct switchdev_attr *attr,
8904c08c586SVladimir Oltean 				      struct netlink_ext_ack *extack))
switchdev_bridge_port_offload(struct net_device * brport_dev,struct net_device * dev,const void * ctx,struct notifier_block * atomic_nb,struct notifier_block * blocking_nb,bool tx_fwd_offload,struct netlink_ext_ack * extack)8911cb33af1SFlorian Fainelli {
8921cb33af1SFlorian Fainelli 	int err;
8931cb33af1SFlorian Fainelli 
8941cb33af1SFlorian Fainelli 	err = __switchdev_handle_port_attr_set(dev, port_attr_info, check_cb,
8951cb33af1SFlorian Fainelli 					       set_cb);
8961cb33af1SFlorian Fainelli 	if (err == -EOPNOTSUPP)
8971cb33af1SFlorian Fainelli 		err = 0;
8981cb33af1SFlorian Fainelli 	return err;
8991cb33af1SFlorian Fainelli }
9001cb33af1SFlorian Fainelli EXPORT_SYMBOL_GPL(switchdev_handle_port_attr_set);
901957e2235SVladimir Oltean 
902957e2235SVladimir Oltean int switchdev_bridge_port_offload(struct net_device *brport_dev,
903957e2235SVladimir Oltean 				  struct net_device *dev, const void *ctx,
904957e2235SVladimir Oltean 				  struct notifier_block *atomic_nb,
905957e2235SVladimir Oltean 				  struct notifier_block *blocking_nb,
906957e2235SVladimir Oltean 				  bool tx_fwd_offload,
907957e2235SVladimir Oltean 				  struct netlink_ext_ack *extack)
908957e2235SVladimir Oltean {
909957e2235SVladimir Oltean 	struct switchdev_notifier_brport_info brport_info = {
910957e2235SVladimir Oltean 		.brport = {
911957e2235SVladimir Oltean 			.dev = dev,
912957e2235SVladimir Oltean 			.ctx = ctx,
913957e2235SVladimir Oltean 			.atomic_nb = atomic_nb,
914957e2235SVladimir Oltean 			.blocking_nb = blocking_nb,
915957e2235SVladimir Oltean 			.tx_fwd_offload = tx_fwd_offload,
916957e2235SVladimir Oltean 		},
917957e2235SVladimir Oltean 	};
switchdev_bridge_port_unoffload(struct net_device * brport_dev,const void * ctx,struct notifier_block * atomic_nb,struct notifier_block * blocking_nb)918957e2235SVladimir Oltean 	int err;
919957e2235SVladimir Oltean 
920957e2235SVladimir Oltean 	ASSERT_RTNL();
921957e2235SVladimir Oltean 
922957e2235SVladimir Oltean 	err = call_switchdev_blocking_notifiers(SWITCHDEV_BRPORT_OFFLOADED,
923957e2235SVladimir Oltean 						brport_dev, &brport_info.info,
924957e2235SVladimir Oltean 						extack);
925957e2235SVladimir Oltean 	return notifier_to_errno(err);
926957e2235SVladimir Oltean }
927957e2235SVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_bridge_port_offload);
928957e2235SVladimir Oltean 
929957e2235SVladimir Oltean void switchdev_bridge_port_unoffload(struct net_device *brport_dev,
930957e2235SVladimir Oltean 				     const void *ctx,
931957e2235SVladimir Oltean 				     struct notifier_block *atomic_nb,
932957e2235SVladimir Oltean 				     struct notifier_block *blocking_nb)
933957e2235SVladimir Oltean {
934957e2235SVladimir Oltean 	struct switchdev_notifier_brport_info brport_info = {
935957e2235SVladimir Oltean 		.brport = {
936957e2235SVladimir Oltean 			.ctx = ctx,
937957e2235SVladimir Oltean 			.atomic_nb = atomic_nb,
938957e2235SVladimir Oltean 			.blocking_nb = blocking_nb,
switchdev_bridge_port_replay(struct net_device * brport_dev,struct net_device * dev,const void * ctx,struct notifier_block * atomic_nb,struct notifier_block * blocking_nb,struct netlink_ext_ack * extack)939957e2235SVladimir Oltean 		},
940957e2235SVladimir Oltean 	};
941957e2235SVladimir Oltean 
942957e2235SVladimir Oltean 	ASSERT_RTNL();
943957e2235SVladimir Oltean 
944957e2235SVladimir Oltean 	call_switchdev_blocking_notifiers(SWITCHDEV_BRPORT_UNOFFLOADED,
945957e2235SVladimir Oltean 					  brport_dev, &brport_info.info,
946957e2235SVladimir Oltean 					  NULL);
947957e2235SVladimir Oltean }
948957e2235SVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_bridge_port_unoffload);
949f2e2857bSPetr Machata 
950f2e2857bSPetr Machata int switchdev_bridge_port_replay(struct net_device *brport_dev,
951f2e2857bSPetr Machata 				 struct net_device *dev, const void *ctx,
952f2e2857bSPetr Machata 				 struct notifier_block *atomic_nb,
953f2e2857bSPetr Machata 				 struct notifier_block *blocking_nb,
954f2e2857bSPetr Machata 				 struct netlink_ext_ack *extack)
955f2e2857bSPetr Machata {
956f2e2857bSPetr Machata 	struct switchdev_notifier_brport_info brport_info = {
957f2e2857bSPetr Machata 		.brport = {
958f2e2857bSPetr Machata 			.dev = dev,
959f2e2857bSPetr Machata 			.ctx = ctx,
960f2e2857bSPetr Machata 			.atomic_nb = atomic_nb,
961f2e2857bSPetr Machata 			.blocking_nb = blocking_nb,
962f2e2857bSPetr Machata 		},
963f2e2857bSPetr Machata 	};
964f2e2857bSPetr Machata 	int err;
965f2e2857bSPetr Machata 
966f2e2857bSPetr Machata 	ASSERT_RTNL();
967f2e2857bSPetr Machata 
968f2e2857bSPetr Machata 	err = call_switchdev_blocking_notifiers(SWITCHDEV_BRPORT_REPLAY,
969f2e2857bSPetr Machata 						brport_dev, &brport_info.info,
970f2e2857bSPetr Machata 						extack);
971f2e2857bSPetr Machata 	return notifier_to_errno(err);
972f2e2857bSPetr Machata }
973f2e2857bSPetr Machata EXPORT_SYMBOL_GPL(switchdev_bridge_port_replay);
974