xref: /linux/net/switchdev/switchdev.c (revision b7ffab29)
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)22dc489f86STobias Waldekranz static bool switchdev_obj_eq(const struct switchdev_obj *a,
23dc489f86STobias Waldekranz 			     const struct switchdev_obj *b)
24dc489f86STobias Waldekranz {
25dc489f86STobias Waldekranz 	const struct switchdev_obj_port_vlan *va, *vb;
26dc489f86STobias Waldekranz 	const struct switchdev_obj_port_mdb *ma, *mb;
27dc489f86STobias Waldekranz 
28dc489f86STobias Waldekranz 	if (a->id != b->id || a->orig_dev != b->orig_dev)
29dc489f86STobias Waldekranz 		return false;
30dc489f86STobias Waldekranz 
31dc489f86STobias Waldekranz 	switch (a->id) {
32dc489f86STobias Waldekranz 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
33dc489f86STobias Waldekranz 		va = SWITCHDEV_OBJ_PORT_VLAN(a);
34dc489f86STobias Waldekranz 		vb = SWITCHDEV_OBJ_PORT_VLAN(b);
35dc489f86STobias Waldekranz 		return va->flags == vb->flags &&
36dc489f86STobias Waldekranz 			va->vid == vb->vid &&
37dc489f86STobias Waldekranz 			va->changed == vb->changed;
38dc489f86STobias Waldekranz 	case SWITCHDEV_OBJ_ID_PORT_MDB:
39dc489f86STobias Waldekranz 	case SWITCHDEV_OBJ_ID_HOST_MDB:
40dc489f86STobias Waldekranz 		ma = SWITCHDEV_OBJ_PORT_MDB(a);
41dc489f86STobias Waldekranz 		mb = SWITCHDEV_OBJ_PORT_MDB(b);
42dc489f86STobias Waldekranz 		return ma->vid == mb->vid &&
43dc489f86STobias Waldekranz 			ether_addr_equal(ma->addr, mb->addr);
44dc489f86STobias Waldekranz 	default:
45dc489f86STobias Waldekranz 		break;
46dc489f86STobias Waldekranz 	}
47dc489f86STobias Waldekranz 
48dc489f86STobias Waldekranz 	BUG();
49dc489f86STobias Waldekranz }
50dc489f86STobias 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_obj_id_to_helpful_msg(struct net_device * dev,enum switchdev_obj_id obj_id,int err,bool add)247*b7ffab29SOleksij Rempel static void switchdev_obj_id_to_helpful_msg(struct net_device *dev,
248*b7ffab29SOleksij Rempel 					    enum switchdev_obj_id obj_id,
249*b7ffab29SOleksij Rempel 					    int err, bool add)
250*b7ffab29SOleksij Rempel {
251*b7ffab29SOleksij Rempel 	const char *action = add ? "add" : "del";
252*b7ffab29SOleksij Rempel 	const char *reason = "";
253*b7ffab29SOleksij Rempel 	const char *problem;
254*b7ffab29SOleksij Rempel 	const char *obj_str;
255*b7ffab29SOleksij Rempel 
256*b7ffab29SOleksij Rempel 	switch (obj_id) {
257*b7ffab29SOleksij Rempel 	case SWITCHDEV_OBJ_ID_UNDEFINED:
258*b7ffab29SOleksij Rempel 		obj_str = "Undefined object";
259*b7ffab29SOleksij Rempel 		problem = "Attempted operation is undefined, indicating a possible programming\n"
260*b7ffab29SOleksij Rempel 			  "error.\n";
261*b7ffab29SOleksij Rempel 		break;
262*b7ffab29SOleksij Rempel 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
263*b7ffab29SOleksij Rempel 		obj_str = "VLAN entry";
264*b7ffab29SOleksij Rempel 		problem = "Failure in VLAN settings on this port might disrupt network\n"
265*b7ffab29SOleksij Rempel 			  "segmentation or traffic isolation, affecting network partitioning.\n";
266*b7ffab29SOleksij Rempel 		break;
267*b7ffab29SOleksij Rempel 	case SWITCHDEV_OBJ_ID_PORT_MDB:
268*b7ffab29SOleksij Rempel 		obj_str = "Port Multicast Database entry";
269*b7ffab29SOleksij Rempel 		problem = "Failure in updating the port's Multicast Database could lead to\n"
270*b7ffab29SOleksij Rempel 			  "multicast forwarding issues.\n";
271*b7ffab29SOleksij Rempel 		break;
272*b7ffab29SOleksij Rempel 	case SWITCHDEV_OBJ_ID_HOST_MDB:
273*b7ffab29SOleksij Rempel 		obj_str = "Host Multicast Database entry";
274*b7ffab29SOleksij Rempel 		problem = "Failure in updating the host's Multicast Database may impact multicast\n"
275*b7ffab29SOleksij Rempel 			  "group memberships or traffic delivery, affecting multicast\n"
276*b7ffab29SOleksij Rempel 			  "communication.\n";
277*b7ffab29SOleksij Rempel 		break;
278*b7ffab29SOleksij Rempel 	case SWITCHDEV_OBJ_ID_MRP:
279*b7ffab29SOleksij Rempel 		obj_str = "Media Redundancy Protocol configuration for port";
280*b7ffab29SOleksij Rempel 		problem = "Failure to set MRP ring ID on this port prevents communication with\n"
281*b7ffab29SOleksij Rempel 			  "the specified redundancy ring, resulting in an inability to engage\n"
282*b7ffab29SOleksij Rempel 			  "in MRP-based network operations.\n";
283*b7ffab29SOleksij Rempel 		break;
284*b7ffab29SOleksij Rempel 	case SWITCHDEV_OBJ_ID_RING_TEST_MRP:
285*b7ffab29SOleksij Rempel 		obj_str = "MRP Test Frame Operations for port";
286*b7ffab29SOleksij Rempel 		problem = "Failure to generate/monitor MRP test frames may lead to inability to\n"
287*b7ffab29SOleksij Rempel 			  "assess the ring's operational integrity and fault response, hindering\n"
288*b7ffab29SOleksij Rempel 			  "proactive network management.\n";
289*b7ffab29SOleksij Rempel 		break;
290*b7ffab29SOleksij Rempel 	case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
291*b7ffab29SOleksij Rempel 		obj_str = "MRP Ring Role Configuration";
292*b7ffab29SOleksij Rempel 		problem = "Improper MRP ring role configuration may create conflicts in the ring,\n"
293*b7ffab29SOleksij Rempel 			  "disrupting communication for all participants, or isolate the local\n"
294*b7ffab29SOleksij Rempel 			  "system from the ring, hindering its ability to communicate with other\n"
295*b7ffab29SOleksij Rempel 			  "participants.\n";
296*b7ffab29SOleksij Rempel 		break;
297*b7ffab29SOleksij Rempel 	case SWITCHDEV_OBJ_ID_RING_STATE_MRP:
298*b7ffab29SOleksij Rempel 		obj_str = "MRP Ring State Configuration";
299*b7ffab29SOleksij Rempel 		problem = "Failure to correctly set the MRP ring state can result in network\n"
300*b7ffab29SOleksij Rempel 			  "loops or leave segments without communication. In a Closed state,\n"
301*b7ffab29SOleksij Rempel 			  "it maintains loop prevention by blocking one MRM port, while an Open\n"
302*b7ffab29SOleksij Rempel 			  "state activates in response to failures, changing port states to\n"
303*b7ffab29SOleksij Rempel 			  "preserve network connectivity.\n";
304*b7ffab29SOleksij Rempel 		break;
305*b7ffab29SOleksij Rempel 	case SWITCHDEV_OBJ_ID_IN_TEST_MRP:
306*b7ffab29SOleksij Rempel 		obj_str = "MRP_InTest Frame Generation Configuration";
307*b7ffab29SOleksij Rempel 		problem = "Failure in managing MRP_InTest frame generation can misjudge the\n"
308*b7ffab29SOleksij Rempel 			  "interconnection ring's state, leading to incorrect blocking or\n"
309*b7ffab29SOleksij Rempel 			  "unblocking of the I/C port. This misconfiguration might result\n"
310*b7ffab29SOleksij Rempel 			  "in unintended network loops or isolate critical network segments,\n"
311*b7ffab29SOleksij Rempel 			  "compromising network integrity and reliability.\n";
312*b7ffab29SOleksij Rempel 		break;
313*b7ffab29SOleksij Rempel 	case SWITCHDEV_OBJ_ID_IN_ROLE_MRP:
314*b7ffab29SOleksij Rempel 		obj_str = "Interconnection Ring Role Configuration";
315*b7ffab29SOleksij Rempel 		problem = "Failure in incorrect assignment of interconnection ring roles\n"
316*b7ffab29SOleksij Rempel 			  "(MIM/MIC) can impair the formation of the interconnection rings.\n";
317*b7ffab29SOleksij Rempel 		break;
318*b7ffab29SOleksij Rempel 	case SWITCHDEV_OBJ_ID_IN_STATE_MRP:
319*b7ffab29SOleksij Rempel 		obj_str = "Interconnection Ring State Configuration";
320*b7ffab29SOleksij Rempel 		problem = "Failure in updating the interconnection ring state can lead in\n"
321*b7ffab29SOleksij Rempel 			  "case of Open state to incorrect blocking or unblocking of the\n"
322*b7ffab29SOleksij Rempel 			  "I/C port, resulting in unintended network loops or isolation\n"
323*b7ffab29SOleksij Rempel 			  "of critical network\n";
324*b7ffab29SOleksij Rempel 		break;
325*b7ffab29SOleksij Rempel 	default:
326*b7ffab29SOleksij Rempel 		obj_str = "Unknown object";
327*b7ffab29SOleksij Rempel 		problem	= "Indicating a possible programming error.\n";
328*b7ffab29SOleksij Rempel 	}
329*b7ffab29SOleksij Rempel 
330*b7ffab29SOleksij Rempel 	switch (err) {
331*b7ffab29SOleksij Rempel 	case -ENOSPC:
332*b7ffab29SOleksij Rempel 		reason = "Current HW/SW setup lacks sufficient resources.\n";
333*b7ffab29SOleksij Rempel 		break;
334*b7ffab29SOleksij Rempel 	}
335*b7ffab29SOleksij Rempel 
336*b7ffab29SOleksij Rempel 	netdev_err(dev, "Failed to %s %s (object id=%d) with error: %pe (%d).\n%s%s\n",
337*b7ffab29SOleksij Rempel 		   action, obj_str, obj_id, ERR_PTR(err), err, problem, reason);
338*b7ffab29SOleksij Rempel }
339*b7ffab29SOleksij Rempel 
switchdev_port_obj_add_deferred(struct net_device * dev,const void * data)3404d429c5dSJiri Pirko static void switchdev_port_obj_add_deferred(struct net_device *dev,
3414d429c5dSJiri Pirko 					    const void *data)
3424d429c5dSJiri Pirko {
3434d429c5dSJiri Pirko 	const struct switchdev_obj *obj = data;
3444d429c5dSJiri Pirko 	int err;
3454d429c5dSJiri Pirko 
346cf6def51SVladimir Oltean 	ASSERT_RTNL();
347cf6def51SVladimir Oltean 	err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD,
348cf6def51SVladimir Oltean 					dev, obj, NULL);
3494d429c5dSJiri Pirko 	if (err && err != -EOPNOTSUPP)
350*b7ffab29SOleksij Rempel 		switchdev_obj_id_to_helpful_msg(dev, obj->id, err, true);
3517ceb2afbSElad Raz 	if (obj->complete)
3527ceb2afbSElad Raz 		obj->complete(dev, err, obj->complete_priv);
3534d429c5dSJiri Pirko }
3544d429c5dSJiri Pirko 
switchdev_port_obj_add_defer(struct net_device * dev,const struct switchdev_obj * obj)3554d429c5dSJiri Pirko static int switchdev_port_obj_add_defer(struct net_device *dev,
3564d429c5dSJiri Pirko 					const struct switchdev_obj *obj)
3574d429c5dSJiri Pirko {
358e258d919SScott Feldman 	return switchdev_deferred_enqueue(dev, obj, switchdev_obj_size(obj),
3594d429c5dSJiri Pirko 					  switchdev_port_obj_add_deferred);
3604d429c5dSJiri Pirko }
361491d0f15SScott Feldman 
362491d0f15SScott Feldman /**
3634d429c5dSJiri Pirko  *	switchdev_port_obj_add - Add port object
364491d0f15SScott Feldman  *
365491d0f15SScott Feldman  *	@dev: port device
3664d429c5dSJiri Pirko  *	@obj: object to add
367c8af73f0SAndrew Lunn  *	@extack: netlink extended ack
3684d429c5dSJiri Pirko  *
3694d429c5dSJiri Pirko  *	rtnl_lock must be held and must not be in atomic section,
3704d429c5dSJiri Pirko  *	in case SWITCHDEV_F_DEFER flag is not set.
371491d0f15SScott Feldman  */
switchdev_port_obj_add(struct net_device * dev,const struct switchdev_obj * obj,struct netlink_ext_ack * extack)3724d429c5dSJiri Pirko int switchdev_port_obj_add(struct net_device *dev,
37369b7320eSPetr Machata 			   const struct switchdev_obj *obj,
37469b7320eSPetr Machata 			   struct netlink_ext_ack *extack)
3754d429c5dSJiri Pirko {
3764d429c5dSJiri Pirko 	if (obj->flags & SWITCHDEV_F_DEFER)
3774d429c5dSJiri Pirko 		return switchdev_port_obj_add_defer(dev, obj);
3784d429c5dSJiri Pirko 	ASSERT_RTNL();
379cf6def51SVladimir Oltean 	return switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD,
380cf6def51SVladimir Oltean 					 dev, obj, extack);
3814d429c5dSJiri Pirko }
3824d429c5dSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_port_obj_add);
3834d429c5dSJiri Pirko 
switchdev_port_obj_del_now(struct net_device * dev,const struct switchdev_obj * obj)3844d429c5dSJiri Pirko static int switchdev_port_obj_del_now(struct net_device *dev,
385648b4a99SJiri Pirko 				      const struct switchdev_obj *obj)
386491d0f15SScott Feldman {
387d17d9f5eSPetr Machata 	return switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_DEL,
388ffb68fc5SVladimir Oltean 					 dev, obj, NULL);
389491d0f15SScott Feldman }
3904d429c5dSJiri Pirko 
switchdev_port_obj_del_deferred(struct net_device * dev,const void * data)3914d429c5dSJiri Pirko static void switchdev_port_obj_del_deferred(struct net_device *dev,
3924d429c5dSJiri Pirko 					    const void *data)
3934d429c5dSJiri Pirko {
3944d429c5dSJiri Pirko 	const struct switchdev_obj *obj = data;
3954d429c5dSJiri Pirko 	int err;
3964d429c5dSJiri Pirko 
3974d429c5dSJiri Pirko 	err = switchdev_port_obj_del_now(dev, obj);
3984d429c5dSJiri Pirko 	if (err && err != -EOPNOTSUPP)
399*b7ffab29SOleksij Rempel 		switchdev_obj_id_to_helpful_msg(dev, obj->id, err, false);
4007ceb2afbSElad Raz 	if (obj->complete)
4017ceb2afbSElad Raz 		obj->complete(dev, err, obj->complete_priv);
4024d429c5dSJiri Pirko }
4034d429c5dSJiri Pirko 
switchdev_port_obj_del_defer(struct net_device * dev,const struct switchdev_obj * obj)4044d429c5dSJiri Pirko static int switchdev_port_obj_del_defer(struct net_device *dev,
4054d429c5dSJiri Pirko 					const struct switchdev_obj *obj)
4064d429c5dSJiri Pirko {
407e258d919SScott Feldman 	return switchdev_deferred_enqueue(dev, obj, switchdev_obj_size(obj),
4084d429c5dSJiri Pirko 					  switchdev_port_obj_del_deferred);
4094d429c5dSJiri Pirko }
4104d429c5dSJiri Pirko 
4114d429c5dSJiri Pirko /**
4124d429c5dSJiri Pirko  *	switchdev_port_obj_del - Delete port object
4134d429c5dSJiri Pirko  *
4144d429c5dSJiri Pirko  *	@dev: port device
4154d429c5dSJiri Pirko  *	@obj: object to delete
4164d429c5dSJiri Pirko  *
4174d429c5dSJiri Pirko  *	rtnl_lock must be held and must not be in atomic section,
4184d429c5dSJiri Pirko  *	in case SWITCHDEV_F_DEFER flag is not set.
4194d429c5dSJiri Pirko  */
switchdev_port_obj_del(struct net_device * dev,const struct switchdev_obj * obj)4204d429c5dSJiri Pirko int switchdev_port_obj_del(struct net_device *dev,
4214d429c5dSJiri Pirko 			   const struct switchdev_obj *obj)
4224d429c5dSJiri Pirko {
4234d429c5dSJiri Pirko 	if (obj->flags & SWITCHDEV_F_DEFER)
4244d429c5dSJiri Pirko 		return switchdev_port_obj_del_defer(dev, obj);
4254d429c5dSJiri Pirko 	ASSERT_RTNL();
4264d429c5dSJiri Pirko 	return switchdev_port_obj_del_now(dev, obj);
4274d429c5dSJiri Pirko }
428491d0f15SScott Feldman EXPORT_SYMBOL_GPL(switchdev_port_obj_del);
429491d0f15SScott Feldman 
430dc489f86STobias Waldekranz /**
431dc489f86STobias Waldekranz  *	switchdev_port_obj_act_is_deferred - Is object action pending?
432dc489f86STobias Waldekranz  *
433dc489f86STobias Waldekranz  *	@dev: port device
434dc489f86STobias Waldekranz  *	@nt: type of action; add or delete
435dc489f86STobias Waldekranz  *	@obj: object to test
436dc489f86STobias Waldekranz  *
437dc489f86STobias Waldekranz  *	Returns true if a deferred item is pending, which is
438dc489f86STobias Waldekranz  *	equivalent to the action @nt on an object @obj.
439dc489f86STobias Waldekranz  *
440dc489f86STobias Waldekranz  *	rtnl_lock must be held.
441dc489f86STobias Waldekranz  */
switchdev_port_obj_act_is_deferred(struct net_device * dev,enum switchdev_notifier_type nt,const struct switchdev_obj * obj)442dc489f86STobias Waldekranz bool switchdev_port_obj_act_is_deferred(struct net_device *dev,
443dc489f86STobias Waldekranz 					enum switchdev_notifier_type nt,
444dc489f86STobias Waldekranz 					const struct switchdev_obj *obj)
445dc489f86STobias Waldekranz {
446dc489f86STobias Waldekranz 	struct switchdev_deferred_item *dfitem;
447dc489f86STobias Waldekranz 	bool found = false;
448dc489f86STobias Waldekranz 
449dc489f86STobias Waldekranz 	ASSERT_RTNL();
450dc489f86STobias Waldekranz 
451dc489f86STobias Waldekranz 	spin_lock_bh(&deferred_lock);
452dc489f86STobias Waldekranz 
453dc489f86STobias Waldekranz 	list_for_each_entry(dfitem, &deferred, list) {
454dc489f86STobias Waldekranz 		if (dfitem->dev != dev)
455dc489f86STobias Waldekranz 			continue;
456dc489f86STobias Waldekranz 
457dc489f86STobias Waldekranz 		if ((dfitem->func == switchdev_port_obj_add_deferred &&
458dc489f86STobias Waldekranz 		     nt == SWITCHDEV_PORT_OBJ_ADD) ||
459dc489f86STobias Waldekranz 		    (dfitem->func == switchdev_port_obj_del_deferred &&
460dc489f86STobias Waldekranz 		     nt == SWITCHDEV_PORT_OBJ_DEL)) {
461dc489f86STobias Waldekranz 			if (switchdev_obj_eq((const void *)dfitem->data, obj)) {
462dc489f86STobias Waldekranz 				found = true;
463dc489f86STobias Waldekranz 				break;
464dc489f86STobias Waldekranz 			}
465dc489f86STobias Waldekranz 		}
466dc489f86STobias Waldekranz 	}
467dc489f86STobias Waldekranz 
468dc489f86STobias Waldekranz 	spin_unlock_bh(&deferred_lock);
469dc489f86STobias Waldekranz 
470dc489f86STobias Waldekranz 	return found;
471dc489f86STobias Waldekranz }
472dc489f86STobias Waldekranz EXPORT_SYMBOL_GPL(switchdev_port_obj_act_is_deferred);
473dc489f86STobias Waldekranz 
474ff5cf100SArkadi Sharshevsky static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain);
475a93e3b17SPetr Machata static BLOCKING_NOTIFIER_HEAD(switchdev_blocking_notif_chain);
47603bf0c28SJiri Pirko 
47703bf0c28SJiri Pirko /**
478ebb9a03aSJiri Pirko  *	register_switchdev_notifier - Register notifier
47903bf0c28SJiri Pirko  *	@nb: notifier_block
48003bf0c28SJiri Pirko  *
481ff5cf100SArkadi Sharshevsky  *	Register switch device notifier.
48203bf0c28SJiri Pirko  */
register_switchdev_notifier(struct notifier_block * nb)483ebb9a03aSJiri Pirko int register_switchdev_notifier(struct notifier_block *nb)
48403bf0c28SJiri Pirko {
485ff5cf100SArkadi Sharshevsky 	return atomic_notifier_chain_register(&switchdev_notif_chain, nb);
48603bf0c28SJiri Pirko }
487ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(register_switchdev_notifier);
48803bf0c28SJiri Pirko 
48903bf0c28SJiri Pirko /**
490ebb9a03aSJiri Pirko  *	unregister_switchdev_notifier - Unregister notifier
49103bf0c28SJiri Pirko  *	@nb: notifier_block
49203bf0c28SJiri Pirko  *
49303bf0c28SJiri Pirko  *	Unregister switch device notifier.
49403bf0c28SJiri Pirko  */
unregister_switchdev_notifier(struct notifier_block * nb)495ebb9a03aSJiri Pirko int unregister_switchdev_notifier(struct notifier_block *nb)
49603bf0c28SJiri Pirko {
497ff5cf100SArkadi Sharshevsky 	return atomic_notifier_chain_unregister(&switchdev_notif_chain, nb);
49803bf0c28SJiri Pirko }
499ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(unregister_switchdev_notifier);
50003bf0c28SJiri Pirko 
50103bf0c28SJiri Pirko /**
502ebb9a03aSJiri Pirko  *	call_switchdev_notifiers - Call notifiers
50303bf0c28SJiri Pirko  *	@val: value passed unmodified to notifier function
50403bf0c28SJiri Pirko  *	@dev: port device
50503bf0c28SJiri Pirko  *	@info: notifier information data
506ea6754aeSTian Tao  *	@extack: netlink extended ack
507ff5cf100SArkadi Sharshevsky  *	Call all network notifier blocks.
50803bf0c28SJiri Pirko  */
call_switchdev_notifiers(unsigned long val,struct net_device * dev,struct switchdev_notifier_info * info,struct netlink_ext_ack * extack)509ebb9a03aSJiri Pirko int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
5106685987cSPetr Machata 			     struct switchdev_notifier_info *info,
5116685987cSPetr Machata 			     struct netlink_ext_ack *extack)
51203bf0c28SJiri Pirko {
51303bf0c28SJiri Pirko 	info->dev = dev;
5146685987cSPetr Machata 	info->extack = extack;
515ff5cf100SArkadi Sharshevsky 	return atomic_notifier_call_chain(&switchdev_notif_chain, val, info);
51603bf0c28SJiri Pirko }
517ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
5188a44dbb2SRoopa Prabhu 
register_switchdev_blocking_notifier(struct notifier_block * nb)519a93e3b17SPetr Machata int register_switchdev_blocking_notifier(struct notifier_block *nb)
520a93e3b17SPetr Machata {
521a93e3b17SPetr Machata 	struct blocking_notifier_head *chain = &switchdev_blocking_notif_chain;
522a93e3b17SPetr Machata 
523a93e3b17SPetr Machata 	return blocking_notifier_chain_register(chain, nb);
524a93e3b17SPetr Machata }
525a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(register_switchdev_blocking_notifier);
526a93e3b17SPetr Machata 
unregister_switchdev_blocking_notifier(struct notifier_block * nb)527a93e3b17SPetr Machata int unregister_switchdev_blocking_notifier(struct notifier_block *nb)
528a93e3b17SPetr Machata {
529a93e3b17SPetr Machata 	struct blocking_notifier_head *chain = &switchdev_blocking_notif_chain;
530a93e3b17SPetr Machata 
531a93e3b17SPetr Machata 	return blocking_notifier_chain_unregister(chain, nb);
532a93e3b17SPetr Machata }
533a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(unregister_switchdev_blocking_notifier);
534a93e3b17SPetr Machata 
call_switchdev_blocking_notifiers(unsigned long val,struct net_device * dev,struct switchdev_notifier_info * info,struct netlink_ext_ack * extack)535a93e3b17SPetr Machata int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev,
536479c86dcSPetr Machata 				      struct switchdev_notifier_info *info,
537479c86dcSPetr Machata 				      struct netlink_ext_ack *extack)
538a93e3b17SPetr Machata {
539a93e3b17SPetr Machata 	info->dev = dev;
540479c86dcSPetr Machata 	info->extack = extack;
541a93e3b17SPetr Machata 	return blocking_notifier_call_chain(&switchdev_blocking_notif_chain,
542a93e3b17SPetr Machata 					    val, info);
543a93e3b17SPetr Machata }
544a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(call_switchdev_blocking_notifiers);
545a93e3b17SPetr Machata 
5462b0a5688SVladimir Oltean struct switchdev_nested_priv {
5472b0a5688SVladimir Oltean 	bool (*check_cb)(const struct net_device *dev);
5482b0a5688SVladimir Oltean 	bool (*foreign_dev_check_cb)(const struct net_device *dev,
5492b0a5688SVladimir Oltean 				     const struct net_device *foreign_dev);
5502b0a5688SVladimir Oltean 	const struct net_device *dev;
5512b0a5688SVladimir Oltean 	struct net_device *lower_dev;
5522b0a5688SVladimir Oltean };
5532b0a5688SVladimir Oltean 
switchdev_lower_dev_walk(struct net_device * lower_dev,struct netdev_nested_priv * priv)5542b0a5688SVladimir Oltean static int switchdev_lower_dev_walk(struct net_device *lower_dev,
5552b0a5688SVladimir Oltean 				    struct netdev_nested_priv *priv)
5562b0a5688SVladimir Oltean {
5572b0a5688SVladimir Oltean 	struct switchdev_nested_priv *switchdev_priv = priv->data;
5582b0a5688SVladimir Oltean 	bool (*foreign_dev_check_cb)(const struct net_device *dev,
5592b0a5688SVladimir Oltean 				     const struct net_device *foreign_dev);
5602b0a5688SVladimir Oltean 	bool (*check_cb)(const struct net_device *dev);
5612b0a5688SVladimir Oltean 	const struct net_device *dev;
5622b0a5688SVladimir Oltean 
5632b0a5688SVladimir Oltean 	check_cb = switchdev_priv->check_cb;
5642b0a5688SVladimir Oltean 	foreign_dev_check_cb = switchdev_priv->foreign_dev_check_cb;
5652b0a5688SVladimir Oltean 	dev = switchdev_priv->dev;
5662b0a5688SVladimir Oltean 
5672b0a5688SVladimir Oltean 	if (check_cb(lower_dev) && !foreign_dev_check_cb(lower_dev, dev)) {
5682b0a5688SVladimir Oltean 		switchdev_priv->lower_dev = lower_dev;
5692b0a5688SVladimir Oltean 		return 1;
5702b0a5688SVladimir Oltean 	}
5712b0a5688SVladimir Oltean 
5722b0a5688SVladimir Oltean 	return 0;
5732b0a5688SVladimir Oltean }
5742b0a5688SVladimir Oltean 
5752b0a5688SVladimir Oltean static struct net_device *
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))5767b465f4cSVladimir Oltean switchdev_lower_dev_find_rcu(struct net_device *dev,
5772b0a5688SVladimir Oltean 			     bool (*check_cb)(const struct net_device *dev),
5782b0a5688SVladimir Oltean 			     bool (*foreign_dev_check_cb)(const struct net_device *dev,
5792b0a5688SVladimir Oltean 							  const struct net_device *foreign_dev))
5802b0a5688SVladimir Oltean {
5812b0a5688SVladimir Oltean 	struct switchdev_nested_priv switchdev_priv = {
5822b0a5688SVladimir Oltean 		.check_cb = check_cb,
5832b0a5688SVladimir Oltean 		.foreign_dev_check_cb = foreign_dev_check_cb,
5842b0a5688SVladimir Oltean 		.dev = dev,
5852b0a5688SVladimir Oltean 		.lower_dev = NULL,
5862b0a5688SVladimir Oltean 	};
5872b0a5688SVladimir Oltean 	struct netdev_nested_priv priv = {
5882b0a5688SVladimir Oltean 		.data = &switchdev_priv,
5892b0a5688SVladimir Oltean 	};
5902b0a5688SVladimir Oltean 
5912b0a5688SVladimir Oltean 	netdev_walk_all_lower_dev_rcu(dev, switchdev_lower_dev_walk, &priv);
5922b0a5688SVladimir Oltean 
5932b0a5688SVladimir Oltean 	return switchdev_priv.lower_dev;
5942b0a5688SVladimir Oltean }
5952b0a5688SVladimir Oltean 
596c4076cddSVladimir Oltean static struct net_device *
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))597c4076cddSVladimir Oltean switchdev_lower_dev_find(struct net_device *dev,
598c4076cddSVladimir Oltean 			 bool (*check_cb)(const struct net_device *dev),
599c4076cddSVladimir Oltean 			 bool (*foreign_dev_check_cb)(const struct net_device *dev,
600c4076cddSVladimir Oltean 						      const struct net_device *foreign_dev))
601c4076cddSVladimir Oltean {
602c4076cddSVladimir Oltean 	struct switchdev_nested_priv switchdev_priv = {
603c4076cddSVladimir Oltean 		.check_cb = check_cb,
604c4076cddSVladimir Oltean 		.foreign_dev_check_cb = foreign_dev_check_cb,
605c4076cddSVladimir Oltean 		.dev = dev,
606c4076cddSVladimir Oltean 		.lower_dev = NULL,
607c4076cddSVladimir Oltean 	};
608c4076cddSVladimir Oltean 	struct netdev_nested_priv priv = {
609c4076cddSVladimir Oltean 		.data = &switchdev_priv,
610c4076cddSVladimir Oltean 	};
611c4076cddSVladimir Oltean 
612c4076cddSVladimir Oltean 	netdev_walk_all_lower_dev(dev, switchdev_lower_dev_walk, &priv);
613c4076cddSVladimir Oltean 
614c4076cddSVladimir Oltean 	return switchdev_priv.lower_dev;
615c4076cddSVladimir Oltean }
616c4076cddSVladimir Oltean 
__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))617716a30a9SVladimir Oltean static int __switchdev_handle_fdb_event_to_device(struct net_device *dev,
618716a30a9SVladimir Oltean 		struct net_device *orig_dev, unsigned long event,
6198ca07176SVladimir Oltean 		const struct switchdev_notifier_fdb_info *fdb_info,
6208ca07176SVladimir Oltean 		bool (*check_cb)(const struct net_device *dev),
6218ca07176SVladimir Oltean 		bool (*foreign_dev_check_cb)(const struct net_device *dev,
6228ca07176SVladimir Oltean 					     const struct net_device *foreign_dev),
623716a30a9SVladimir Oltean 		int (*mod_cb)(struct net_device *dev, struct net_device *orig_dev,
624716a30a9SVladimir Oltean 			      unsigned long event, const void *ctx,
6258ca07176SVladimir Oltean 			      const struct switchdev_notifier_fdb_info *fdb_info))
6268ca07176SVladimir Oltean {
6278ca07176SVladimir Oltean 	const struct switchdev_notifier_info *info = &fdb_info->info;
628ec638740SVladimir Oltean 	struct net_device *br, *lower_dev, *switchdev;
6298ca07176SVladimir Oltean 	struct list_head *iter;
6308ca07176SVladimir Oltean 	int err = -EOPNOTSUPP;
6318ca07176SVladimir Oltean 
6322b0a5688SVladimir Oltean 	if (check_cb(dev))
633716a30a9SVladimir Oltean 		return mod_cb(dev, orig_dev, event, info->ctx, fdb_info);
6348ca07176SVladimir Oltean 
6358ca07176SVladimir Oltean 	/* Recurse through lower interfaces in case the FDB entry is pointing
636ec638740SVladimir Oltean 	 * towards a bridge or a LAG device.
6378ca07176SVladimir Oltean 	 */
6388ca07176SVladimir Oltean 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
6398ca07176SVladimir Oltean 		/* Do not propagate FDB entries across bridges */
6408ca07176SVladimir Oltean 		if (netif_is_bridge_master(lower_dev))
6418ca07176SVladimir Oltean 			continue;
6428ca07176SVladimir Oltean 
6432b0a5688SVladimir Oltean 		/* Bridge ports might be either us, or LAG interfaces
6442b0a5688SVladimir Oltean 		 * that we offload.
6452b0a5688SVladimir Oltean 		 */
6462b0a5688SVladimir Oltean 		if (!check_cb(lower_dev) &&
6477b465f4cSVladimir Oltean 		    !switchdev_lower_dev_find_rcu(lower_dev, check_cb,
6482b0a5688SVladimir Oltean 						  foreign_dev_check_cb))
6492b0a5688SVladimir Oltean 			continue;
6502b0a5688SVladimir Oltean 
651716a30a9SVladimir Oltean 		err = __switchdev_handle_fdb_event_to_device(lower_dev, orig_dev,
652716a30a9SVladimir Oltean 							     event, fdb_info, check_cb,
6538ca07176SVladimir Oltean 							     foreign_dev_check_cb,
654ec638740SVladimir Oltean 							     mod_cb);
6558ca07176SVladimir Oltean 		if (err && err != -EOPNOTSUPP)
6568ca07176SVladimir Oltean 			return err;
6578ca07176SVladimir Oltean 	}
6588ca07176SVladimir Oltean 
6592b0a5688SVladimir Oltean 	/* Event is neither on a bridge nor a LAG. Check whether it is on an
6602b0a5688SVladimir Oltean 	 * interface that is in a bridge with us.
6612b0a5688SVladimir Oltean 	 */
6622b0a5688SVladimir Oltean 	br = netdev_master_upper_dev_get_rcu(dev);
6632b0a5688SVladimir Oltean 	if (!br || !netif_is_bridge_master(br))
6642b0a5688SVladimir Oltean 		return 0;
6652b0a5688SVladimir Oltean 
666ec638740SVladimir Oltean 	switchdev = switchdev_lower_dev_find_rcu(br, check_cb, foreign_dev_check_cb);
667ec638740SVladimir Oltean 	if (!switchdev)
6682b0a5688SVladimir Oltean 		return 0;
6692b0a5688SVladimir Oltean 
670ec638740SVladimir Oltean 	if (!foreign_dev_check_cb(switchdev, dev))
671ec638740SVladimir Oltean 		return err;
672ec638740SVladimir Oltean 
673716a30a9SVladimir Oltean 	return __switchdev_handle_fdb_event_to_device(br, orig_dev, event, fdb_info,
6742b0a5688SVladimir Oltean 						      check_cb, foreign_dev_check_cb,
675ec638740SVladimir Oltean 						      mod_cb);
6768ca07176SVladimir Oltean }
6778ca07176SVladimir Oltean 
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))678716a30a9SVladimir Oltean int switchdev_handle_fdb_event_to_device(struct net_device *dev, unsigned long event,
6798ca07176SVladimir Oltean 		const struct switchdev_notifier_fdb_info *fdb_info,
6808ca07176SVladimir Oltean 		bool (*check_cb)(const struct net_device *dev),
6818ca07176SVladimir Oltean 		bool (*foreign_dev_check_cb)(const struct net_device *dev,
6828ca07176SVladimir Oltean 					     const struct net_device *foreign_dev),
683716a30a9SVladimir Oltean 		int (*mod_cb)(struct net_device *dev, struct net_device *orig_dev,
684716a30a9SVladimir Oltean 			      unsigned long event, const void *ctx,
6858ca07176SVladimir Oltean 			      const struct switchdev_notifier_fdb_info *fdb_info))
6868ca07176SVladimir Oltean {
6878ca07176SVladimir Oltean 	int err;
6888ca07176SVladimir Oltean 
689716a30a9SVladimir Oltean 	err = __switchdev_handle_fdb_event_to_device(dev, dev, event, fdb_info,
690716a30a9SVladimir Oltean 						     check_cb, foreign_dev_check_cb,
691ec638740SVladimir Oltean 						     mod_cb);
6928ca07176SVladimir Oltean 	if (err == -EOPNOTSUPP)
6938ca07176SVladimir Oltean 		err = 0;
6948ca07176SVladimir Oltean 
6958ca07176SVladimir Oltean 	return err;
6968ca07176SVladimir Oltean }
697716a30a9SVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_handle_fdb_event_to_device);
6988ca07176SVladimir 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),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))699f30f0601SPetr Machata static int __switchdev_handle_port_obj_add(struct net_device *dev,
700f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
701f30f0601SPetr Machata 			bool (*check_cb)(const struct net_device *dev),
702c4076cddSVladimir Oltean 			bool (*foreign_dev_check_cb)(const struct net_device *dev,
703c4076cddSVladimir Oltean 						     const struct net_device *foreign_dev),
70469bfac96SVladimir Oltean 			int (*add_cb)(struct net_device *dev, const void *ctx,
705f30f0601SPetr Machata 				      const struct switchdev_obj *obj,
70669213513SPetr Machata 				      struct netlink_ext_ack *extack))
707f30f0601SPetr Machata {
70869bfac96SVladimir Oltean 	struct switchdev_notifier_info *info = &port_obj_info->info;
709acd8df58SVladimir Oltean 	struct net_device *br, *lower_dev, *switchdev;
71069213513SPetr Machata 	struct netlink_ext_ack *extack;
711f30f0601SPetr Machata 	struct list_head *iter;
712f30f0601SPetr Machata 	int err = -EOPNOTSUPP;
713f30f0601SPetr Machata 
71469bfac96SVladimir Oltean 	extack = switchdev_notifier_info_to_extack(info);
71569213513SPetr Machata 
716f30f0601SPetr Machata 	if (check_cb(dev)) {
71769bfac96SVladimir Oltean 		err = add_cb(dev, info->ctx, port_obj_info->obj, extack);
71820776b46SRasmus Villemoes 		if (err != -EOPNOTSUPP)
719f30f0601SPetr Machata 			port_obj_info->handled = true;
72020776b46SRasmus Villemoes 		return err;
721f30f0601SPetr Machata 	}
722f30f0601SPetr Machata 
723f30f0601SPetr Machata 	/* Switch ports might be stacked under e.g. a LAG. Ignore the
724f30f0601SPetr Machata 	 * unsupported devices, another driver might be able to handle them. But
725f30f0601SPetr Machata 	 * propagate to the callers any hard errors.
726f30f0601SPetr Machata 	 *
727f30f0601SPetr Machata 	 * If the driver does its own bookkeeping of stacked ports, it's not
728f30f0601SPetr Machata 	 * necessary to go through this helper.
729f30f0601SPetr Machata 	 */
730f30f0601SPetr Machata 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
73107c6f980SRussell King 		if (netif_is_bridge_master(lower_dev))
73207c6f980SRussell King 			continue;
73307c6f980SRussell King 
734c4076cddSVladimir Oltean 		/* When searching for switchdev interfaces that are neighbors
735c4076cddSVladimir Oltean 		 * of foreign ones, and @dev is a bridge, do not recurse on the
736c4076cddSVladimir Oltean 		 * foreign interface again, it was already visited.
737c4076cddSVladimir Oltean 		 */
738c4076cddSVladimir Oltean 		if (foreign_dev_check_cb && !check_cb(lower_dev) &&
739c4076cddSVladimir Oltean 		    !switchdev_lower_dev_find(lower_dev, check_cb, foreign_dev_check_cb))
740c4076cddSVladimir Oltean 			continue;
741c4076cddSVladimir Oltean 
742f30f0601SPetr Machata 		err = __switchdev_handle_port_obj_add(lower_dev, port_obj_info,
743c4076cddSVladimir Oltean 						      check_cb, foreign_dev_check_cb,
744c4076cddSVladimir Oltean 						      add_cb);
745f30f0601SPetr Machata 		if (err && err != -EOPNOTSUPP)
746f30f0601SPetr Machata 			return err;
747f30f0601SPetr Machata 	}
748f30f0601SPetr Machata 
749c4076cddSVladimir Oltean 	/* Event is neither on a bridge nor a LAG. Check whether it is on an
750c4076cddSVladimir Oltean 	 * interface that is in a bridge with us.
751c4076cddSVladimir Oltean 	 */
752c4076cddSVladimir Oltean 	if (!foreign_dev_check_cb)
753f30f0601SPetr Machata 		return err;
754c4076cddSVladimir Oltean 
755c4076cddSVladimir Oltean 	br = netdev_master_upper_dev_get(dev);
756c4076cddSVladimir Oltean 	if (!br || !netif_is_bridge_master(br))
757c4076cddSVladimir Oltean 		return err;
758c4076cddSVladimir Oltean 
759acd8df58SVladimir Oltean 	switchdev = switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb);
760acd8df58SVladimir Oltean 	if (!switchdev)
761acd8df58SVladimir Oltean 		return err;
762acd8df58SVladimir Oltean 
763acd8df58SVladimir Oltean 	if (!foreign_dev_check_cb(switchdev, dev))
764c4076cddSVladimir Oltean 		return err;
765c4076cddSVladimir Oltean 
766c4076cddSVladimir Oltean 	return __switchdev_handle_port_obj_add(br, port_obj_info, check_cb,
767c4076cddSVladimir Oltean 					       foreign_dev_check_cb, add_cb);
768f30f0601SPetr Machata }
769f30f0601SPetr Machata 
770c4076cddSVladimir Oltean /* Pass through a port object addition, if @dev passes @check_cb, or replicate
771c4076cddSVladimir Oltean  * it towards all lower interfaces of @dev that pass @check_cb, if @dev is a
772c4076cddSVladimir Oltean  * bridge or a LAG.
773c4076cddSVladimir 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))774f30f0601SPetr Machata int switchdev_handle_port_obj_add(struct net_device *dev,
775f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
776f30f0601SPetr Machata 			bool (*check_cb)(const struct net_device *dev),
77769bfac96SVladimir Oltean 			int (*add_cb)(struct net_device *dev, const void *ctx,
778f30f0601SPetr Machata 				      const struct switchdev_obj *obj,
77969213513SPetr Machata 				      struct netlink_ext_ack *extack))
780f30f0601SPetr Machata {
781f30f0601SPetr Machata 	int err;
782f30f0601SPetr Machata 
783f30f0601SPetr Machata 	err = __switchdev_handle_port_obj_add(dev, port_obj_info, check_cb,
784c4076cddSVladimir Oltean 					      NULL, add_cb);
785f30f0601SPetr Machata 	if (err == -EOPNOTSUPP)
786f30f0601SPetr Machata 		err = 0;
787f30f0601SPetr Machata 	return err;
788f30f0601SPetr Machata }
789f30f0601SPetr Machata EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add);
790f30f0601SPetr Machata 
791c4076cddSVladimir Oltean /* Same as switchdev_handle_port_obj_add(), except if object is notified on a
792c4076cddSVladimir Oltean  * @dev that passes @foreign_dev_check_cb, it is replicated towards all devices
793c4076cddSVladimir Oltean  * that pass @check_cb and are in the same bridge as @dev.
794c4076cddSVladimir Oltean  */
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))795c4076cddSVladimir Oltean int switchdev_handle_port_obj_add_foreign(struct net_device *dev,
796c4076cddSVladimir Oltean 			struct switchdev_notifier_port_obj_info *port_obj_info,
797c4076cddSVladimir Oltean 			bool (*check_cb)(const struct net_device *dev),
798c4076cddSVladimir Oltean 			bool (*foreign_dev_check_cb)(const struct net_device *dev,
799c4076cddSVladimir Oltean 						     const struct net_device *foreign_dev),
800c4076cddSVladimir Oltean 			int (*add_cb)(struct net_device *dev, const void *ctx,
801c4076cddSVladimir Oltean 				      const struct switchdev_obj *obj,
802c4076cddSVladimir Oltean 				      struct netlink_ext_ack *extack))
803c4076cddSVladimir Oltean {
804c4076cddSVladimir Oltean 	int err;
805c4076cddSVladimir Oltean 
806c4076cddSVladimir Oltean 	err = __switchdev_handle_port_obj_add(dev, port_obj_info, check_cb,
807c4076cddSVladimir Oltean 					      foreign_dev_check_cb, add_cb);
808c4076cddSVladimir Oltean 	if (err == -EOPNOTSUPP)
809c4076cddSVladimir Oltean 		err = 0;
810c4076cddSVladimir Oltean 	return err;
811c4076cddSVladimir Oltean }
812c4076cddSVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add_foreign);
813c4076cddSVladimir 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),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))814f30f0601SPetr Machata static int __switchdev_handle_port_obj_del(struct net_device *dev,
815f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
816f30f0601SPetr Machata 			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),
81969bfac96SVladimir Oltean 			int (*del_cb)(struct net_device *dev, const void *ctx,
820f30f0601SPetr Machata 				      const struct switchdev_obj *obj))
821f30f0601SPetr Machata {
82269bfac96SVladimir Oltean 	struct switchdev_notifier_info *info = &port_obj_info->info;
823acd8df58SVladimir Oltean 	struct net_device *br, *lower_dev, *switchdev;
824f30f0601SPetr Machata 	struct list_head *iter;
825f30f0601SPetr Machata 	int err = -EOPNOTSUPP;
826f30f0601SPetr Machata 
827f30f0601SPetr Machata 	if (check_cb(dev)) {
82869bfac96SVladimir Oltean 		err = del_cb(dev, info->ctx, port_obj_info->obj);
82920776b46SRasmus Villemoes 		if (err != -EOPNOTSUPP)
830f30f0601SPetr Machata 			port_obj_info->handled = true;
83120776b46SRasmus Villemoes 		return err;
832f30f0601SPetr Machata 	}
833f30f0601SPetr Machata 
834f30f0601SPetr Machata 	/* Switch ports might be stacked under e.g. a LAG. Ignore the
835f30f0601SPetr Machata 	 * unsupported devices, another driver might be able to handle them. But
836f30f0601SPetr Machata 	 * propagate to the callers any hard errors.
837f30f0601SPetr Machata 	 *
838f30f0601SPetr Machata 	 * If the driver does its own bookkeeping of stacked ports, it's not
839f30f0601SPetr Machata 	 * necessary to go through this helper.
840f30f0601SPetr Machata 	 */
841f30f0601SPetr Machata 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
84207c6f980SRussell King 		if (netif_is_bridge_master(lower_dev))
84307c6f980SRussell King 			continue;
84407c6f980SRussell King 
845c4076cddSVladimir Oltean 		/* When searching for switchdev interfaces that are neighbors
846c4076cddSVladimir Oltean 		 * of foreign ones, and @dev is a bridge, do not recurse on the
847c4076cddSVladimir Oltean 		 * foreign interface again, it was already visited.
848c4076cddSVladimir Oltean 		 */
849c4076cddSVladimir Oltean 		if (foreign_dev_check_cb && !check_cb(lower_dev) &&
850c4076cddSVladimir Oltean 		    !switchdev_lower_dev_find(lower_dev, check_cb, foreign_dev_check_cb))
851c4076cddSVladimir Oltean 			continue;
852c4076cddSVladimir Oltean 
853f30f0601SPetr Machata 		err = __switchdev_handle_port_obj_del(lower_dev, port_obj_info,
854c4076cddSVladimir Oltean 						      check_cb, foreign_dev_check_cb,
855c4076cddSVladimir Oltean 						      del_cb);
856f30f0601SPetr Machata 		if (err && err != -EOPNOTSUPP)
857f30f0601SPetr Machata 			return err;
858f30f0601SPetr Machata 	}
859f30f0601SPetr Machata 
860c4076cddSVladimir Oltean 	/* Event is neither on a bridge nor a LAG. Check whether it is on an
861c4076cddSVladimir Oltean 	 * interface that is in a bridge with us.
862c4076cddSVladimir Oltean 	 */
863c4076cddSVladimir Oltean 	if (!foreign_dev_check_cb)
864f30f0601SPetr Machata 		return err;
865c4076cddSVladimir Oltean 
866c4076cddSVladimir Oltean 	br = netdev_master_upper_dev_get(dev);
867c4076cddSVladimir Oltean 	if (!br || !netif_is_bridge_master(br))
868c4076cddSVladimir Oltean 		return err;
869c4076cddSVladimir Oltean 
870acd8df58SVladimir Oltean 	switchdev = switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb);
871acd8df58SVladimir Oltean 	if (!switchdev)
872acd8df58SVladimir Oltean 		return err;
873acd8df58SVladimir Oltean 
874acd8df58SVladimir Oltean 	if (!foreign_dev_check_cb(switchdev, dev))
875c4076cddSVladimir Oltean 		return err;
876c4076cddSVladimir Oltean 
877c4076cddSVladimir Oltean 	return __switchdev_handle_port_obj_del(br, port_obj_info, check_cb,
878c4076cddSVladimir Oltean 					       foreign_dev_check_cb, del_cb);
879f30f0601SPetr Machata }
880f30f0601SPetr Machata 
881c4076cddSVladimir Oltean /* Pass through a port object deletion, if @dev passes @check_cb, or replicate
882c4076cddSVladimir Oltean  * it towards all lower interfaces of @dev that pass @check_cb, if @dev is a
883c4076cddSVladimir Oltean  * bridge or a LAG.
884c4076cddSVladimir 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))885f30f0601SPetr Machata int switchdev_handle_port_obj_del(struct net_device *dev,
886f30f0601SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info,
887f30f0601SPetr Machata 			bool (*check_cb)(const struct net_device *dev),
88869bfac96SVladimir Oltean 			int (*del_cb)(struct net_device *dev, const void *ctx,
889f30f0601SPetr Machata 				      const struct switchdev_obj *obj))
890f30f0601SPetr Machata {
891f30f0601SPetr Machata 	int err;
892f30f0601SPetr Machata 
893f30f0601SPetr Machata 	err = __switchdev_handle_port_obj_del(dev, port_obj_info, check_cb,
894c4076cddSVladimir Oltean 					      NULL, del_cb);
895f30f0601SPetr Machata 	if (err == -EOPNOTSUPP)
896f30f0601SPetr Machata 		err = 0;
897f30f0601SPetr Machata 	return err;
898f30f0601SPetr Machata }
899f30f0601SPetr Machata EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del);
9001cb33af1SFlorian Fainelli 
901c4076cddSVladimir Oltean /* Same as switchdev_handle_port_obj_del(), except if object is notified on a
902c4076cddSVladimir Oltean  * @dev that passes @foreign_dev_check_cb, it is replicated towards all devices
903c4076cddSVladimir Oltean  * that pass @check_cb and are in the same bridge as @dev.
904c4076cddSVladimir Oltean  */
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))905c4076cddSVladimir Oltean int switchdev_handle_port_obj_del_foreign(struct net_device *dev,
906c4076cddSVladimir Oltean 			struct switchdev_notifier_port_obj_info *port_obj_info,
907c4076cddSVladimir Oltean 			bool (*check_cb)(const struct net_device *dev),
908c4076cddSVladimir Oltean 			bool (*foreign_dev_check_cb)(const struct net_device *dev,
909c4076cddSVladimir Oltean 						     const struct net_device *foreign_dev),
910c4076cddSVladimir Oltean 			int (*del_cb)(struct net_device *dev, const void *ctx,
911c4076cddSVladimir Oltean 				      const struct switchdev_obj *obj))
912c4076cddSVladimir Oltean {
913c4076cddSVladimir Oltean 	int err;
914c4076cddSVladimir Oltean 
915c4076cddSVladimir Oltean 	err = __switchdev_handle_port_obj_del(dev, port_obj_info, check_cb,
916c4076cddSVladimir Oltean 					      foreign_dev_check_cb, del_cb);
917c4076cddSVladimir Oltean 	if (err == -EOPNOTSUPP)
918c4076cddSVladimir Oltean 		err = 0;
919c4076cddSVladimir Oltean 	return err;
920c4076cddSVladimir Oltean }
921c4076cddSVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del_foreign);
922c4076cddSVladimir Oltean 
__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))9231cb33af1SFlorian Fainelli static int __switchdev_handle_port_attr_set(struct net_device *dev,
9241cb33af1SFlorian Fainelli 			struct switchdev_notifier_port_attr_info *port_attr_info,
9251cb33af1SFlorian Fainelli 			bool (*check_cb)(const struct net_device *dev),
92669bfac96SVladimir Oltean 			int (*set_cb)(struct net_device *dev, const void *ctx,
9274c08c586SVladimir Oltean 				      const struct switchdev_attr *attr,
9284c08c586SVladimir Oltean 				      struct netlink_ext_ack *extack))
9291cb33af1SFlorian Fainelli {
93069bfac96SVladimir Oltean 	struct switchdev_notifier_info *info = &port_attr_info->info;
9314c08c586SVladimir Oltean 	struct netlink_ext_ack *extack;
9321cb33af1SFlorian Fainelli 	struct net_device *lower_dev;
9331cb33af1SFlorian Fainelli 	struct list_head *iter;
9341cb33af1SFlorian Fainelli 	int err = -EOPNOTSUPP;
9351cb33af1SFlorian Fainelli 
93669bfac96SVladimir Oltean 	extack = switchdev_notifier_info_to_extack(info);
9374c08c586SVladimir Oltean 
9381cb33af1SFlorian Fainelli 	if (check_cb(dev)) {
93969bfac96SVladimir Oltean 		err = set_cb(dev, info->ctx, port_attr_info->attr, extack);
94020776b46SRasmus Villemoes 		if (err != -EOPNOTSUPP)
9411cb33af1SFlorian Fainelli 			port_attr_info->handled = true;
94220776b46SRasmus Villemoes 		return err;
9431cb33af1SFlorian Fainelli 	}
9441cb33af1SFlorian Fainelli 
9451cb33af1SFlorian Fainelli 	/* Switch ports might be stacked under e.g. a LAG. Ignore the
9461cb33af1SFlorian Fainelli 	 * unsupported devices, another driver might be able to handle them. But
9471cb33af1SFlorian Fainelli 	 * propagate to the callers any hard errors.
9481cb33af1SFlorian Fainelli 	 *
9491cb33af1SFlorian Fainelli 	 * If the driver does its own bookkeeping of stacked ports, it's not
9501cb33af1SFlorian Fainelli 	 * necessary to go through this helper.
9511cb33af1SFlorian Fainelli 	 */
9521cb33af1SFlorian Fainelli 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
95307c6f980SRussell King 		if (netif_is_bridge_master(lower_dev))
95407c6f980SRussell King 			continue;
95507c6f980SRussell King 
9561cb33af1SFlorian Fainelli 		err = __switchdev_handle_port_attr_set(lower_dev, port_attr_info,
9571cb33af1SFlorian Fainelli 						       check_cb, set_cb);
9581cb33af1SFlorian Fainelli 		if (err && err != -EOPNOTSUPP)
9591cb33af1SFlorian Fainelli 			return err;
9601cb33af1SFlorian Fainelli 	}
9611cb33af1SFlorian Fainelli 
9621cb33af1SFlorian Fainelli 	return err;
9631cb33af1SFlorian Fainelli }
9641cb33af1SFlorian Fainelli 
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))9651cb33af1SFlorian Fainelli int switchdev_handle_port_attr_set(struct net_device *dev,
9661cb33af1SFlorian Fainelli 			struct switchdev_notifier_port_attr_info *port_attr_info,
9671cb33af1SFlorian Fainelli 			bool (*check_cb)(const struct net_device *dev),
96869bfac96SVladimir Oltean 			int (*set_cb)(struct net_device *dev, const void *ctx,
9694c08c586SVladimir Oltean 				      const struct switchdev_attr *attr,
9704c08c586SVladimir Oltean 				      struct netlink_ext_ack *extack))
9711cb33af1SFlorian Fainelli {
9721cb33af1SFlorian Fainelli 	int err;
9731cb33af1SFlorian Fainelli 
9741cb33af1SFlorian Fainelli 	err = __switchdev_handle_port_attr_set(dev, port_attr_info, check_cb,
9751cb33af1SFlorian Fainelli 					       set_cb);
9761cb33af1SFlorian Fainelli 	if (err == -EOPNOTSUPP)
9771cb33af1SFlorian Fainelli 		err = 0;
9781cb33af1SFlorian Fainelli 	return err;
9791cb33af1SFlorian Fainelli }
9801cb33af1SFlorian Fainelli EXPORT_SYMBOL_GPL(switchdev_handle_port_attr_set);
981957e2235SVladimir Oltean 
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)982957e2235SVladimir Oltean int switchdev_bridge_port_offload(struct net_device *brport_dev,
983957e2235SVladimir Oltean 				  struct net_device *dev, const void *ctx,
984957e2235SVladimir Oltean 				  struct notifier_block *atomic_nb,
985957e2235SVladimir Oltean 				  struct notifier_block *blocking_nb,
986957e2235SVladimir Oltean 				  bool tx_fwd_offload,
987957e2235SVladimir Oltean 				  struct netlink_ext_ack *extack)
988957e2235SVladimir Oltean {
989957e2235SVladimir Oltean 	struct switchdev_notifier_brport_info brport_info = {
990957e2235SVladimir Oltean 		.brport = {
991957e2235SVladimir Oltean 			.dev = dev,
992957e2235SVladimir Oltean 			.ctx = ctx,
993957e2235SVladimir Oltean 			.atomic_nb = atomic_nb,
994957e2235SVladimir Oltean 			.blocking_nb = blocking_nb,
995957e2235SVladimir Oltean 			.tx_fwd_offload = tx_fwd_offload,
996957e2235SVladimir Oltean 		},
997957e2235SVladimir Oltean 	};
998957e2235SVladimir Oltean 	int err;
999957e2235SVladimir Oltean 
1000957e2235SVladimir Oltean 	ASSERT_RTNL();
1001957e2235SVladimir Oltean 
1002957e2235SVladimir Oltean 	err = call_switchdev_blocking_notifiers(SWITCHDEV_BRPORT_OFFLOADED,
1003957e2235SVladimir Oltean 						brport_dev, &brport_info.info,
1004957e2235SVladimir Oltean 						extack);
1005957e2235SVladimir Oltean 	return notifier_to_errno(err);
1006957e2235SVladimir Oltean }
1007957e2235SVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_bridge_port_offload);
1008957e2235SVladimir Oltean 
switchdev_bridge_port_unoffload(struct net_device * brport_dev,const void * ctx,struct notifier_block * atomic_nb,struct notifier_block * blocking_nb)1009957e2235SVladimir Oltean void switchdev_bridge_port_unoffload(struct net_device *brport_dev,
1010957e2235SVladimir Oltean 				     const void *ctx,
1011957e2235SVladimir Oltean 				     struct notifier_block *atomic_nb,
1012957e2235SVladimir Oltean 				     struct notifier_block *blocking_nb)
1013957e2235SVladimir Oltean {
1014957e2235SVladimir Oltean 	struct switchdev_notifier_brport_info brport_info = {
1015957e2235SVladimir Oltean 		.brport = {
1016957e2235SVladimir Oltean 			.ctx = ctx,
1017957e2235SVladimir Oltean 			.atomic_nb = atomic_nb,
1018957e2235SVladimir Oltean 			.blocking_nb = blocking_nb,
1019957e2235SVladimir Oltean 		},
1020957e2235SVladimir Oltean 	};
1021957e2235SVladimir Oltean 
1022957e2235SVladimir Oltean 	ASSERT_RTNL();
1023957e2235SVladimir Oltean 
1024957e2235SVladimir Oltean 	call_switchdev_blocking_notifiers(SWITCHDEV_BRPORT_UNOFFLOADED,
1025957e2235SVladimir Oltean 					  brport_dev, &brport_info.info,
1026957e2235SVladimir Oltean 					  NULL);
1027957e2235SVladimir Oltean }
1028957e2235SVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_bridge_port_unoffload);
1029f2e2857bSPetr Machata 
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)1030f2e2857bSPetr Machata int switchdev_bridge_port_replay(struct net_device *brport_dev,
1031f2e2857bSPetr Machata 				 struct net_device *dev, const void *ctx,
1032f2e2857bSPetr Machata 				 struct notifier_block *atomic_nb,
1033f2e2857bSPetr Machata 				 struct notifier_block *blocking_nb,
1034f2e2857bSPetr Machata 				 struct netlink_ext_ack *extack)
1035f2e2857bSPetr Machata {
1036f2e2857bSPetr Machata 	struct switchdev_notifier_brport_info brport_info = {
1037f2e2857bSPetr Machata 		.brport = {
1038f2e2857bSPetr Machata 			.dev = dev,
1039f2e2857bSPetr Machata 			.ctx = ctx,
1040f2e2857bSPetr Machata 			.atomic_nb = atomic_nb,
1041f2e2857bSPetr Machata 			.blocking_nb = blocking_nb,
1042f2e2857bSPetr Machata 		},
1043f2e2857bSPetr Machata 	};
1044f2e2857bSPetr Machata 	int err;
1045f2e2857bSPetr Machata 
1046f2e2857bSPetr Machata 	ASSERT_RTNL();
1047f2e2857bSPetr Machata 
1048f2e2857bSPetr Machata 	err = call_switchdev_blocking_notifiers(SWITCHDEV_BRPORT_REPLAY,
1049f2e2857bSPetr Machata 						brport_dev, &brport_info.info,
1050f2e2857bSPetr Machata 						extack);
1051f2e2857bSPetr Machata 	return notifier_to_errno(err);
1052f2e2857bSPetr Machata }
1053f2e2857bSPetr Machata EXPORT_SYMBOL_GPL(switchdev_bridge_port_replay);
1054