12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2007f790cSJiri Pirko /*
3007f790cSJiri Pirko * net/switchdev/switchdev.c - Switch device API
47ea6eb3fSJiri Pirko * Copyright (c) 2014-2015 Jiri Pirko <[email protected]>
5f8f21471SScott Feldman * Copyright (c) 2014-2015 Scott Feldman <[email protected]>
6007f790cSJiri Pirko */
7007f790cSJiri Pirko
8007f790cSJiri Pirko #include <linux/kernel.h>
9007f790cSJiri Pirko #include <linux/types.h>
10007f790cSJiri Pirko #include <linux/init.h>
1103bf0c28SJiri Pirko #include <linux/mutex.h>
1203bf0c28SJiri Pirko #include <linux/notifier.h>
13007f790cSJiri Pirko #include <linux/netdevice.h>
14850d0cbcSJiri Pirko #include <linux/etherdevice.h>
1547f8328bSScott Feldman #include <linux/if_bridge.h>
167ea6eb3fSJiri Pirko #include <linux/list.h>
17793f4014SJiri Pirko #include <linux/workqueue.h>
1887aaf2caSNikolay Aleksandrov #include <linux/if_vlan.h>
194f2c6ae5SIdo Schimmel #include <linux/rtnetlink.h>
20007f790cSJiri Pirko #include <net/switchdev.h>
21007f790cSJiri Pirko
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)247b7ffab29SOleksij Rempel static void switchdev_obj_id_to_helpful_msg(struct net_device *dev,
248b7ffab29SOleksij Rempel enum switchdev_obj_id obj_id,
249b7ffab29SOleksij Rempel int err, bool add)
250b7ffab29SOleksij Rempel {
251b7ffab29SOleksij Rempel const char *action = add ? "add" : "del";
252b7ffab29SOleksij Rempel const char *reason = "";
253b7ffab29SOleksij Rempel const char *problem;
254b7ffab29SOleksij Rempel const char *obj_str;
255b7ffab29SOleksij Rempel
256b7ffab29SOleksij Rempel switch (obj_id) {
257b7ffab29SOleksij Rempel case SWITCHDEV_OBJ_ID_UNDEFINED:
258b7ffab29SOleksij Rempel obj_str = "Undefined object";
259b7ffab29SOleksij Rempel problem = "Attempted operation is undefined, indicating a possible programming\n"
260b7ffab29SOleksij Rempel "error.\n";
261b7ffab29SOleksij Rempel break;
262b7ffab29SOleksij Rempel case SWITCHDEV_OBJ_ID_PORT_VLAN:
263b7ffab29SOleksij Rempel obj_str = "VLAN entry";
264b7ffab29SOleksij Rempel problem = "Failure in VLAN settings on this port might disrupt network\n"
265b7ffab29SOleksij Rempel "segmentation or traffic isolation, affecting network partitioning.\n";
266b7ffab29SOleksij Rempel break;
267b7ffab29SOleksij Rempel case SWITCHDEV_OBJ_ID_PORT_MDB:
268b7ffab29SOleksij Rempel obj_str = "Port Multicast Database entry";
269b7ffab29SOleksij Rempel problem = "Failure in updating the port's Multicast Database could lead to\n"
270b7ffab29SOleksij Rempel "multicast forwarding issues.\n";
271b7ffab29SOleksij Rempel break;
272b7ffab29SOleksij Rempel case SWITCHDEV_OBJ_ID_HOST_MDB:
273b7ffab29SOleksij Rempel obj_str = "Host Multicast Database entry";
274b7ffab29SOleksij Rempel problem = "Failure in updating the host's Multicast Database may impact multicast\n"
275b7ffab29SOleksij Rempel "group memberships or traffic delivery, affecting multicast\n"
276b7ffab29SOleksij Rempel "communication.\n";
277b7ffab29SOleksij Rempel break;
278b7ffab29SOleksij Rempel case SWITCHDEV_OBJ_ID_MRP:
279b7ffab29SOleksij Rempel obj_str = "Media Redundancy Protocol configuration for port";
280b7ffab29SOleksij Rempel problem = "Failure to set MRP ring ID on this port prevents communication with\n"
281b7ffab29SOleksij Rempel "the specified redundancy ring, resulting in an inability to engage\n"
282b7ffab29SOleksij Rempel "in MRP-based network operations.\n";
283b7ffab29SOleksij Rempel break;
284b7ffab29SOleksij Rempel case SWITCHDEV_OBJ_ID_RING_TEST_MRP:
285b7ffab29SOleksij Rempel obj_str = "MRP Test Frame Operations for port";
286b7ffab29SOleksij Rempel problem = "Failure to generate/monitor MRP test frames may lead to inability to\n"
287b7ffab29SOleksij Rempel "assess the ring's operational integrity and fault response, hindering\n"
288b7ffab29SOleksij Rempel "proactive network management.\n";
289b7ffab29SOleksij Rempel break;
290b7ffab29SOleksij Rempel case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
291b7ffab29SOleksij Rempel obj_str = "MRP Ring Role Configuration";
292b7ffab29SOleksij Rempel problem = "Improper MRP ring role configuration may create conflicts in the ring,\n"
293b7ffab29SOleksij Rempel "disrupting communication for all participants, or isolate the local\n"
294b7ffab29SOleksij Rempel "system from the ring, hindering its ability to communicate with other\n"
295b7ffab29SOleksij Rempel "participants.\n";
296b7ffab29SOleksij Rempel break;
297b7ffab29SOleksij Rempel case SWITCHDEV_OBJ_ID_RING_STATE_MRP:
298b7ffab29SOleksij Rempel obj_str = "MRP Ring State Configuration";
299b7ffab29SOleksij Rempel problem = "Failure to correctly set the MRP ring state can result in network\n"
300b7ffab29SOleksij Rempel "loops or leave segments without communication. In a Closed state,\n"
301b7ffab29SOleksij Rempel "it maintains loop prevention by blocking one MRM port, while an Open\n"
302b7ffab29SOleksij Rempel "state activates in response to failures, changing port states to\n"
303b7ffab29SOleksij Rempel "preserve network connectivity.\n";
304b7ffab29SOleksij Rempel break;
305b7ffab29SOleksij Rempel case SWITCHDEV_OBJ_ID_IN_TEST_MRP:
306b7ffab29SOleksij Rempel obj_str = "MRP_InTest Frame Generation Configuration";
307b7ffab29SOleksij Rempel problem = "Failure in managing MRP_InTest frame generation can misjudge the\n"
308b7ffab29SOleksij Rempel "interconnection ring's state, leading to incorrect blocking or\n"
309b7ffab29SOleksij Rempel "unblocking of the I/C port. This misconfiguration might result\n"
310b7ffab29SOleksij Rempel "in unintended network loops or isolate critical network segments,\n"
311b7ffab29SOleksij Rempel "compromising network integrity and reliability.\n";
312b7ffab29SOleksij Rempel break;
313b7ffab29SOleksij Rempel case SWITCHDEV_OBJ_ID_IN_ROLE_MRP:
314b7ffab29SOleksij Rempel obj_str = "Interconnection Ring Role Configuration";
315b7ffab29SOleksij Rempel problem = "Failure in incorrect assignment of interconnection ring roles\n"
316b7ffab29SOleksij Rempel "(MIM/MIC) can impair the formation of the interconnection rings.\n";
317b7ffab29SOleksij Rempel break;
318b7ffab29SOleksij Rempel case SWITCHDEV_OBJ_ID_IN_STATE_MRP:
319b7ffab29SOleksij Rempel obj_str = "Interconnection Ring State Configuration";
320b7ffab29SOleksij Rempel problem = "Failure in updating the interconnection ring state can lead in\n"
321b7ffab29SOleksij Rempel "case of Open state to incorrect blocking or unblocking of the\n"
322b7ffab29SOleksij Rempel "I/C port, resulting in unintended network loops or isolation\n"
323b7ffab29SOleksij Rempel "of critical network\n";
324b7ffab29SOleksij Rempel break;
325b7ffab29SOleksij Rempel default:
326b7ffab29SOleksij Rempel obj_str = "Unknown object";
327b7ffab29SOleksij Rempel problem = "Indicating a possible programming error.\n";
328b7ffab29SOleksij Rempel }
329b7ffab29SOleksij Rempel
330b7ffab29SOleksij Rempel switch (err) {
331b7ffab29SOleksij Rempel case -ENOSPC:
332b7ffab29SOleksij Rempel reason = "Current HW/SW setup lacks sufficient resources.\n";
333b7ffab29SOleksij Rempel break;
334b7ffab29SOleksij Rempel }
335b7ffab29SOleksij Rempel
336b7ffab29SOleksij Rempel netdev_err(dev, "Failed to %s %s (object id=%d) with error: %pe (%d).\n%s%s\n",
337b7ffab29SOleksij Rempel action, obj_str, obj_id, ERR_PTR(err), err, problem, reason);
338b7ffab29SOleksij Rempel }
339b7ffab29SOleksij 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)
350b7ffab29SOleksij 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)
399b7ffab29SOleksij 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);
475*62531a1eSAmit Cohen static RAW_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 {
521*62531a1eSAmit Cohen struct raw_notifier_head *chain = &switchdev_blocking_notif_chain;
522*62531a1eSAmit Cohen int err;
523a93e3b17SPetr Machata
524*62531a1eSAmit Cohen rtnl_lock();
525*62531a1eSAmit Cohen err = raw_notifier_chain_register(chain, nb);
526*62531a1eSAmit Cohen rtnl_unlock();
527*62531a1eSAmit Cohen
528*62531a1eSAmit Cohen return err;
529a93e3b17SPetr Machata }
530a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(register_switchdev_blocking_notifier);
531a93e3b17SPetr Machata
unregister_switchdev_blocking_notifier(struct notifier_block * nb)532a93e3b17SPetr Machata int unregister_switchdev_blocking_notifier(struct notifier_block *nb)
533a93e3b17SPetr Machata {
534*62531a1eSAmit Cohen struct raw_notifier_head *chain = &switchdev_blocking_notif_chain;
535*62531a1eSAmit Cohen int err;
536a93e3b17SPetr Machata
537*62531a1eSAmit Cohen rtnl_lock();
538*62531a1eSAmit Cohen err = raw_notifier_chain_unregister(chain, nb);
539*62531a1eSAmit Cohen rtnl_unlock();
540*62531a1eSAmit Cohen
541*62531a1eSAmit Cohen return err;
542a93e3b17SPetr Machata }
543a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(unregister_switchdev_blocking_notifier);
544a93e3b17SPetr Machata
call_switchdev_blocking_notifiers(unsigned long val,struct net_device * dev,struct switchdev_notifier_info * info,struct netlink_ext_ack * extack)545a93e3b17SPetr Machata int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev,
546479c86dcSPetr Machata struct switchdev_notifier_info *info,
547479c86dcSPetr Machata struct netlink_ext_ack *extack)
548a93e3b17SPetr Machata {
549*62531a1eSAmit Cohen ASSERT_RTNL();
550a93e3b17SPetr Machata info->dev = dev;
551479c86dcSPetr Machata info->extack = extack;
552*62531a1eSAmit Cohen return raw_notifier_call_chain(&switchdev_blocking_notif_chain,
553a93e3b17SPetr Machata val, info);
554a93e3b17SPetr Machata }
555a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(call_switchdev_blocking_notifiers);
556a93e3b17SPetr Machata
5572b0a5688SVladimir Oltean struct switchdev_nested_priv {
5582b0a5688SVladimir Oltean bool (*check_cb)(const struct net_device *dev);
5592b0a5688SVladimir Oltean bool (*foreign_dev_check_cb)(const struct net_device *dev,
5602b0a5688SVladimir Oltean const struct net_device *foreign_dev);
5612b0a5688SVladimir Oltean const struct net_device *dev;
5622b0a5688SVladimir Oltean struct net_device *lower_dev;
5632b0a5688SVladimir Oltean };
5642b0a5688SVladimir Oltean
switchdev_lower_dev_walk(struct net_device * lower_dev,struct netdev_nested_priv * priv)5652b0a5688SVladimir Oltean static int switchdev_lower_dev_walk(struct net_device *lower_dev,
5662b0a5688SVladimir Oltean struct netdev_nested_priv *priv)
5672b0a5688SVladimir Oltean {
5682b0a5688SVladimir Oltean struct switchdev_nested_priv *switchdev_priv = priv->data;
5692b0a5688SVladimir Oltean bool (*foreign_dev_check_cb)(const struct net_device *dev,
5702b0a5688SVladimir Oltean const struct net_device *foreign_dev);
5712b0a5688SVladimir Oltean bool (*check_cb)(const struct net_device *dev);
5722b0a5688SVladimir Oltean const struct net_device *dev;
5732b0a5688SVladimir Oltean
5742b0a5688SVladimir Oltean check_cb = switchdev_priv->check_cb;
5752b0a5688SVladimir Oltean foreign_dev_check_cb = switchdev_priv->foreign_dev_check_cb;
5762b0a5688SVladimir Oltean dev = switchdev_priv->dev;
5772b0a5688SVladimir Oltean
5782b0a5688SVladimir Oltean if (check_cb(lower_dev) && !foreign_dev_check_cb(lower_dev, dev)) {
5792b0a5688SVladimir Oltean switchdev_priv->lower_dev = lower_dev;
5802b0a5688SVladimir Oltean return 1;
5812b0a5688SVladimir Oltean }
5822b0a5688SVladimir Oltean
5832b0a5688SVladimir Oltean return 0;
5842b0a5688SVladimir Oltean }
5852b0a5688SVladimir Oltean
5862b0a5688SVladimir 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))5877b465f4cSVladimir Oltean switchdev_lower_dev_find_rcu(struct net_device *dev,
5882b0a5688SVladimir Oltean bool (*check_cb)(const struct net_device *dev),
5892b0a5688SVladimir Oltean bool (*foreign_dev_check_cb)(const struct net_device *dev,
5902b0a5688SVladimir Oltean const struct net_device *foreign_dev))
5912b0a5688SVladimir Oltean {
5922b0a5688SVladimir Oltean struct switchdev_nested_priv switchdev_priv = {
5932b0a5688SVladimir Oltean .check_cb = check_cb,
5942b0a5688SVladimir Oltean .foreign_dev_check_cb = foreign_dev_check_cb,
5952b0a5688SVladimir Oltean .dev = dev,
5962b0a5688SVladimir Oltean .lower_dev = NULL,
5972b0a5688SVladimir Oltean };
5982b0a5688SVladimir Oltean struct netdev_nested_priv priv = {
5992b0a5688SVladimir Oltean .data = &switchdev_priv,
6002b0a5688SVladimir Oltean };
6012b0a5688SVladimir Oltean
6022b0a5688SVladimir Oltean netdev_walk_all_lower_dev_rcu(dev, switchdev_lower_dev_walk, &priv);
6032b0a5688SVladimir Oltean
6042b0a5688SVladimir Oltean return switchdev_priv.lower_dev;
6052b0a5688SVladimir Oltean }
6062b0a5688SVladimir Oltean
607c4076cddSVladimir 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))608c4076cddSVladimir Oltean switchdev_lower_dev_find(struct net_device *dev,
609c4076cddSVladimir Oltean bool (*check_cb)(const struct net_device *dev),
610c4076cddSVladimir Oltean bool (*foreign_dev_check_cb)(const struct net_device *dev,
611c4076cddSVladimir Oltean const struct net_device *foreign_dev))
612c4076cddSVladimir Oltean {
613c4076cddSVladimir Oltean struct switchdev_nested_priv switchdev_priv = {
614c4076cddSVladimir Oltean .check_cb = check_cb,
615c4076cddSVladimir Oltean .foreign_dev_check_cb = foreign_dev_check_cb,
616c4076cddSVladimir Oltean .dev = dev,
617c4076cddSVladimir Oltean .lower_dev = NULL,
618c4076cddSVladimir Oltean };
619c4076cddSVladimir Oltean struct netdev_nested_priv priv = {
620c4076cddSVladimir Oltean .data = &switchdev_priv,
621c4076cddSVladimir Oltean };
622c4076cddSVladimir Oltean
623c4076cddSVladimir Oltean netdev_walk_all_lower_dev(dev, switchdev_lower_dev_walk, &priv);
624c4076cddSVladimir Oltean
625c4076cddSVladimir Oltean return switchdev_priv.lower_dev;
626c4076cddSVladimir Oltean }
627c4076cddSVladimir 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))628716a30a9SVladimir Oltean static int __switchdev_handle_fdb_event_to_device(struct net_device *dev,
629716a30a9SVladimir Oltean struct net_device *orig_dev, unsigned long event,
6308ca07176SVladimir Oltean const struct switchdev_notifier_fdb_info *fdb_info,
6318ca07176SVladimir Oltean bool (*check_cb)(const struct net_device *dev),
6328ca07176SVladimir Oltean bool (*foreign_dev_check_cb)(const struct net_device *dev,
6338ca07176SVladimir Oltean const struct net_device *foreign_dev),
634716a30a9SVladimir Oltean int (*mod_cb)(struct net_device *dev, struct net_device *orig_dev,
635716a30a9SVladimir Oltean unsigned long event, const void *ctx,
6368ca07176SVladimir Oltean const struct switchdev_notifier_fdb_info *fdb_info))
6378ca07176SVladimir Oltean {
6388ca07176SVladimir Oltean const struct switchdev_notifier_info *info = &fdb_info->info;
639ec638740SVladimir Oltean struct net_device *br, *lower_dev, *switchdev;
6408ca07176SVladimir Oltean struct list_head *iter;
6418ca07176SVladimir Oltean int err = -EOPNOTSUPP;
6428ca07176SVladimir Oltean
6432b0a5688SVladimir Oltean if (check_cb(dev))
644716a30a9SVladimir Oltean return mod_cb(dev, orig_dev, event, info->ctx, fdb_info);
6458ca07176SVladimir Oltean
6468ca07176SVladimir Oltean /* Recurse through lower interfaces in case the FDB entry is pointing
647ec638740SVladimir Oltean * towards a bridge or a LAG device.
6488ca07176SVladimir Oltean */
6498ca07176SVladimir Oltean netdev_for_each_lower_dev(dev, lower_dev, iter) {
6508ca07176SVladimir Oltean /* Do not propagate FDB entries across bridges */
6518ca07176SVladimir Oltean if (netif_is_bridge_master(lower_dev))
6528ca07176SVladimir Oltean continue;
6538ca07176SVladimir Oltean
6542b0a5688SVladimir Oltean /* Bridge ports might be either us, or LAG interfaces
6552b0a5688SVladimir Oltean * that we offload.
6562b0a5688SVladimir Oltean */
6572b0a5688SVladimir Oltean if (!check_cb(lower_dev) &&
6587b465f4cSVladimir Oltean !switchdev_lower_dev_find_rcu(lower_dev, check_cb,
6592b0a5688SVladimir Oltean foreign_dev_check_cb))
6602b0a5688SVladimir Oltean continue;
6612b0a5688SVladimir Oltean
662716a30a9SVladimir Oltean err = __switchdev_handle_fdb_event_to_device(lower_dev, orig_dev,
663716a30a9SVladimir Oltean event, fdb_info, check_cb,
6648ca07176SVladimir Oltean foreign_dev_check_cb,
665ec638740SVladimir Oltean mod_cb);
6668ca07176SVladimir Oltean if (err && err != -EOPNOTSUPP)
6678ca07176SVladimir Oltean return err;
6688ca07176SVladimir Oltean }
6698ca07176SVladimir Oltean
6702b0a5688SVladimir Oltean /* Event is neither on a bridge nor a LAG. Check whether it is on an
6712b0a5688SVladimir Oltean * interface that is in a bridge with us.
6722b0a5688SVladimir Oltean */
6732b0a5688SVladimir Oltean br = netdev_master_upper_dev_get_rcu(dev);
6742b0a5688SVladimir Oltean if (!br || !netif_is_bridge_master(br))
6752b0a5688SVladimir Oltean return 0;
6762b0a5688SVladimir Oltean
677ec638740SVladimir Oltean switchdev = switchdev_lower_dev_find_rcu(br, check_cb, foreign_dev_check_cb);
678ec638740SVladimir Oltean if (!switchdev)
6792b0a5688SVladimir Oltean return 0;
6802b0a5688SVladimir Oltean
681ec638740SVladimir Oltean if (!foreign_dev_check_cb(switchdev, dev))
682ec638740SVladimir Oltean return err;
683ec638740SVladimir Oltean
684716a30a9SVladimir Oltean return __switchdev_handle_fdb_event_to_device(br, orig_dev, event, fdb_info,
6852b0a5688SVladimir Oltean check_cb, foreign_dev_check_cb,
686ec638740SVladimir Oltean mod_cb);
6878ca07176SVladimir Oltean }
6888ca07176SVladimir 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))689716a30a9SVladimir Oltean int switchdev_handle_fdb_event_to_device(struct net_device *dev, unsigned long event,
6908ca07176SVladimir Oltean const struct switchdev_notifier_fdb_info *fdb_info,
6918ca07176SVladimir Oltean bool (*check_cb)(const struct net_device *dev),
6928ca07176SVladimir Oltean bool (*foreign_dev_check_cb)(const struct net_device *dev,
6938ca07176SVladimir Oltean const struct net_device *foreign_dev),
694716a30a9SVladimir Oltean int (*mod_cb)(struct net_device *dev, struct net_device *orig_dev,
695716a30a9SVladimir Oltean unsigned long event, const void *ctx,
6968ca07176SVladimir Oltean const struct switchdev_notifier_fdb_info *fdb_info))
6978ca07176SVladimir Oltean {
6988ca07176SVladimir Oltean int err;
6998ca07176SVladimir Oltean
700716a30a9SVladimir Oltean err = __switchdev_handle_fdb_event_to_device(dev, dev, event, fdb_info,
701716a30a9SVladimir Oltean check_cb, foreign_dev_check_cb,
702ec638740SVladimir Oltean mod_cb);
7038ca07176SVladimir Oltean if (err == -EOPNOTSUPP)
7048ca07176SVladimir Oltean err = 0;
7058ca07176SVladimir Oltean
7068ca07176SVladimir Oltean return err;
7078ca07176SVladimir Oltean }
708716a30a9SVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_handle_fdb_event_to_device);
7098ca07176SVladimir 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))710f30f0601SPetr Machata static int __switchdev_handle_port_obj_add(struct net_device *dev,
711f30f0601SPetr Machata struct switchdev_notifier_port_obj_info *port_obj_info,
712f30f0601SPetr Machata bool (*check_cb)(const struct net_device *dev),
713c4076cddSVladimir Oltean bool (*foreign_dev_check_cb)(const struct net_device *dev,
714c4076cddSVladimir Oltean const struct net_device *foreign_dev),
71569bfac96SVladimir Oltean int (*add_cb)(struct net_device *dev, const void *ctx,
716f30f0601SPetr Machata const struct switchdev_obj *obj,
71769213513SPetr Machata struct netlink_ext_ack *extack))
718f30f0601SPetr Machata {
71969bfac96SVladimir Oltean struct switchdev_notifier_info *info = &port_obj_info->info;
720acd8df58SVladimir Oltean struct net_device *br, *lower_dev, *switchdev;
72169213513SPetr Machata struct netlink_ext_ack *extack;
722f30f0601SPetr Machata struct list_head *iter;
723f30f0601SPetr Machata int err = -EOPNOTSUPP;
724f30f0601SPetr Machata
72569bfac96SVladimir Oltean extack = switchdev_notifier_info_to_extack(info);
72669213513SPetr Machata
727f30f0601SPetr Machata if (check_cb(dev)) {
72869bfac96SVladimir Oltean err = add_cb(dev, info->ctx, port_obj_info->obj, extack);
72920776b46SRasmus Villemoes if (err != -EOPNOTSUPP)
730f30f0601SPetr Machata port_obj_info->handled = true;
73120776b46SRasmus Villemoes return err;
732f30f0601SPetr Machata }
733f30f0601SPetr Machata
734f30f0601SPetr Machata /* Switch ports might be stacked under e.g. a LAG. Ignore the
735f30f0601SPetr Machata * unsupported devices, another driver might be able to handle them. But
736f30f0601SPetr Machata * propagate to the callers any hard errors.
737f30f0601SPetr Machata *
738f30f0601SPetr Machata * If the driver does its own bookkeeping of stacked ports, it's not
739f30f0601SPetr Machata * necessary to go through this helper.
740f30f0601SPetr Machata */
741f30f0601SPetr Machata netdev_for_each_lower_dev(dev, lower_dev, iter) {
74207c6f980SRussell King if (netif_is_bridge_master(lower_dev))
74307c6f980SRussell King continue;
74407c6f980SRussell King
745c4076cddSVladimir Oltean /* When searching for switchdev interfaces that are neighbors
746c4076cddSVladimir Oltean * of foreign ones, and @dev is a bridge, do not recurse on the
747c4076cddSVladimir Oltean * foreign interface again, it was already visited.
748c4076cddSVladimir Oltean */
749c4076cddSVladimir Oltean if (foreign_dev_check_cb && !check_cb(lower_dev) &&
750c4076cddSVladimir Oltean !switchdev_lower_dev_find(lower_dev, check_cb, foreign_dev_check_cb))
751c4076cddSVladimir Oltean continue;
752c4076cddSVladimir Oltean
753f30f0601SPetr Machata err = __switchdev_handle_port_obj_add(lower_dev, port_obj_info,
754c4076cddSVladimir Oltean check_cb, foreign_dev_check_cb,
755c4076cddSVladimir Oltean add_cb);
756f30f0601SPetr Machata if (err && err != -EOPNOTSUPP)
757f30f0601SPetr Machata return err;
758f30f0601SPetr Machata }
759f30f0601SPetr Machata
760c4076cddSVladimir Oltean /* Event is neither on a bridge nor a LAG. Check whether it is on an
761c4076cddSVladimir Oltean * interface that is in a bridge with us.
762c4076cddSVladimir Oltean */
763c4076cddSVladimir Oltean if (!foreign_dev_check_cb)
764f30f0601SPetr Machata return err;
765c4076cddSVladimir Oltean
766c4076cddSVladimir Oltean br = netdev_master_upper_dev_get(dev);
767c4076cddSVladimir Oltean if (!br || !netif_is_bridge_master(br))
768c4076cddSVladimir Oltean return err;
769c4076cddSVladimir Oltean
770acd8df58SVladimir Oltean switchdev = switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb);
771acd8df58SVladimir Oltean if (!switchdev)
772acd8df58SVladimir Oltean return err;
773acd8df58SVladimir Oltean
774acd8df58SVladimir Oltean if (!foreign_dev_check_cb(switchdev, dev))
775c4076cddSVladimir Oltean return err;
776c4076cddSVladimir Oltean
777c4076cddSVladimir Oltean return __switchdev_handle_port_obj_add(br, port_obj_info, check_cb,
778c4076cddSVladimir Oltean foreign_dev_check_cb, add_cb);
779f30f0601SPetr Machata }
780f30f0601SPetr Machata
781c4076cddSVladimir Oltean /* Pass through a port object addition, if @dev passes @check_cb, or replicate
782c4076cddSVladimir Oltean * it towards all lower interfaces of @dev that pass @check_cb, if @dev is a
783c4076cddSVladimir Oltean * bridge or a LAG.
784c4076cddSVladimir 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))785f30f0601SPetr Machata int switchdev_handle_port_obj_add(struct net_device *dev,
786f30f0601SPetr Machata struct switchdev_notifier_port_obj_info *port_obj_info,
787f30f0601SPetr Machata bool (*check_cb)(const struct net_device *dev),
78869bfac96SVladimir Oltean int (*add_cb)(struct net_device *dev, const void *ctx,
789f30f0601SPetr Machata const struct switchdev_obj *obj,
79069213513SPetr Machata struct netlink_ext_ack *extack))
791f30f0601SPetr Machata {
792f30f0601SPetr Machata int err;
793f30f0601SPetr Machata
794f30f0601SPetr Machata err = __switchdev_handle_port_obj_add(dev, port_obj_info, check_cb,
795c4076cddSVladimir Oltean NULL, add_cb);
796f30f0601SPetr Machata if (err == -EOPNOTSUPP)
797f30f0601SPetr Machata err = 0;
798f30f0601SPetr Machata return err;
799f30f0601SPetr Machata }
800f30f0601SPetr Machata EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add);
801f30f0601SPetr Machata
802c4076cddSVladimir Oltean /* Same as switchdev_handle_port_obj_add(), except if object is notified on a
803c4076cddSVladimir Oltean * @dev that passes @foreign_dev_check_cb, it is replicated towards all devices
804c4076cddSVladimir Oltean * that pass @check_cb and are in the same bridge as @dev.
805c4076cddSVladimir 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))806c4076cddSVladimir Oltean int switchdev_handle_port_obj_add_foreign(struct net_device *dev,
807c4076cddSVladimir Oltean struct switchdev_notifier_port_obj_info *port_obj_info,
808c4076cddSVladimir Oltean bool (*check_cb)(const struct net_device *dev),
809c4076cddSVladimir Oltean bool (*foreign_dev_check_cb)(const struct net_device *dev,
810c4076cddSVladimir Oltean const struct net_device *foreign_dev),
811c4076cddSVladimir Oltean int (*add_cb)(struct net_device *dev, const void *ctx,
812c4076cddSVladimir Oltean const struct switchdev_obj *obj,
813c4076cddSVladimir Oltean struct netlink_ext_ack *extack))
814c4076cddSVladimir Oltean {
815c4076cddSVladimir Oltean int err;
816c4076cddSVladimir Oltean
817c4076cddSVladimir Oltean err = __switchdev_handle_port_obj_add(dev, port_obj_info, check_cb,
818c4076cddSVladimir Oltean foreign_dev_check_cb, add_cb);
819c4076cddSVladimir Oltean if (err == -EOPNOTSUPP)
820c4076cddSVladimir Oltean err = 0;
821c4076cddSVladimir Oltean return err;
822c4076cddSVladimir Oltean }
823c4076cddSVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add_foreign);
824c4076cddSVladimir 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))825f30f0601SPetr Machata static int __switchdev_handle_port_obj_del(struct net_device *dev,
826f30f0601SPetr Machata struct switchdev_notifier_port_obj_info *port_obj_info,
827f30f0601SPetr Machata 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),
83069bfac96SVladimir Oltean int (*del_cb)(struct net_device *dev, const void *ctx,
831f30f0601SPetr Machata const struct switchdev_obj *obj))
832f30f0601SPetr Machata {
83369bfac96SVladimir Oltean struct switchdev_notifier_info *info = &port_obj_info->info;
834acd8df58SVladimir Oltean struct net_device *br, *lower_dev, *switchdev;
835f30f0601SPetr Machata struct list_head *iter;
836f30f0601SPetr Machata int err = -EOPNOTSUPP;
837f30f0601SPetr Machata
838f30f0601SPetr Machata if (check_cb(dev)) {
83969bfac96SVladimir Oltean err = del_cb(dev, info->ctx, port_obj_info->obj);
84020776b46SRasmus Villemoes if (err != -EOPNOTSUPP)
841f30f0601SPetr Machata port_obj_info->handled = true;
84220776b46SRasmus Villemoes return err;
843f30f0601SPetr Machata }
844f30f0601SPetr Machata
845f30f0601SPetr Machata /* Switch ports might be stacked under e.g. a LAG. Ignore the
846f30f0601SPetr Machata * unsupported devices, another driver might be able to handle them. But
847f30f0601SPetr Machata * propagate to the callers any hard errors.
848f30f0601SPetr Machata *
849f30f0601SPetr Machata * If the driver does its own bookkeeping of stacked ports, it's not
850f30f0601SPetr Machata * necessary to go through this helper.
851f30f0601SPetr Machata */
852f30f0601SPetr Machata netdev_for_each_lower_dev(dev, lower_dev, iter) {
85307c6f980SRussell King if (netif_is_bridge_master(lower_dev))
85407c6f980SRussell King continue;
85507c6f980SRussell King
856c4076cddSVladimir Oltean /* When searching for switchdev interfaces that are neighbors
857c4076cddSVladimir Oltean * of foreign ones, and @dev is a bridge, do not recurse on the
858c4076cddSVladimir Oltean * foreign interface again, it was already visited.
859c4076cddSVladimir Oltean */
860c4076cddSVladimir Oltean if (foreign_dev_check_cb && !check_cb(lower_dev) &&
861c4076cddSVladimir Oltean !switchdev_lower_dev_find(lower_dev, check_cb, foreign_dev_check_cb))
862c4076cddSVladimir Oltean continue;
863c4076cddSVladimir Oltean
864f30f0601SPetr Machata err = __switchdev_handle_port_obj_del(lower_dev, port_obj_info,
865c4076cddSVladimir Oltean check_cb, foreign_dev_check_cb,
866c4076cddSVladimir Oltean del_cb);
867f30f0601SPetr Machata if (err && err != -EOPNOTSUPP)
868f30f0601SPetr Machata return err;
869f30f0601SPetr Machata }
870f30f0601SPetr Machata
871c4076cddSVladimir Oltean /* Event is neither on a bridge nor a LAG. Check whether it is on an
872c4076cddSVladimir Oltean * interface that is in a bridge with us.
873c4076cddSVladimir Oltean */
874c4076cddSVladimir Oltean if (!foreign_dev_check_cb)
875f30f0601SPetr Machata return err;
876c4076cddSVladimir Oltean
877c4076cddSVladimir Oltean br = netdev_master_upper_dev_get(dev);
878c4076cddSVladimir Oltean if (!br || !netif_is_bridge_master(br))
879c4076cddSVladimir Oltean return err;
880c4076cddSVladimir Oltean
881acd8df58SVladimir Oltean switchdev = switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb);
882acd8df58SVladimir Oltean if (!switchdev)
883acd8df58SVladimir Oltean return err;
884acd8df58SVladimir Oltean
885acd8df58SVladimir Oltean if (!foreign_dev_check_cb(switchdev, dev))
886c4076cddSVladimir Oltean return err;
887c4076cddSVladimir Oltean
888c4076cddSVladimir Oltean return __switchdev_handle_port_obj_del(br, port_obj_info, check_cb,
889c4076cddSVladimir Oltean foreign_dev_check_cb, del_cb);
890f30f0601SPetr Machata }
891f30f0601SPetr Machata
892c4076cddSVladimir Oltean /* Pass through a port object deletion, if @dev passes @check_cb, or replicate
893c4076cddSVladimir Oltean * it towards all lower interfaces of @dev that pass @check_cb, if @dev is a
894c4076cddSVladimir Oltean * bridge or a LAG.
895c4076cddSVladimir 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))896f30f0601SPetr Machata int switchdev_handle_port_obj_del(struct net_device *dev,
897f30f0601SPetr Machata struct switchdev_notifier_port_obj_info *port_obj_info,
898f30f0601SPetr Machata bool (*check_cb)(const struct net_device *dev),
89969bfac96SVladimir Oltean int (*del_cb)(struct net_device *dev, const void *ctx,
900f30f0601SPetr Machata const struct switchdev_obj *obj))
901f30f0601SPetr Machata {
902f30f0601SPetr Machata int err;
903f30f0601SPetr Machata
904f30f0601SPetr Machata err = __switchdev_handle_port_obj_del(dev, port_obj_info, check_cb,
905c4076cddSVladimir Oltean NULL, del_cb);
906f30f0601SPetr Machata if (err == -EOPNOTSUPP)
907f30f0601SPetr Machata err = 0;
908f30f0601SPetr Machata return err;
909f30f0601SPetr Machata }
910f30f0601SPetr Machata EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del);
9111cb33af1SFlorian Fainelli
912c4076cddSVladimir Oltean /* Same as switchdev_handle_port_obj_del(), except if object is notified on a
913c4076cddSVladimir Oltean * @dev that passes @foreign_dev_check_cb, it is replicated towards all devices
914c4076cddSVladimir Oltean * that pass @check_cb and are in the same bridge as @dev.
915c4076cddSVladimir 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))916c4076cddSVladimir Oltean int switchdev_handle_port_obj_del_foreign(struct net_device *dev,
917c4076cddSVladimir Oltean struct switchdev_notifier_port_obj_info *port_obj_info,
918c4076cddSVladimir Oltean bool (*check_cb)(const struct net_device *dev),
919c4076cddSVladimir Oltean bool (*foreign_dev_check_cb)(const struct net_device *dev,
920c4076cddSVladimir Oltean const struct net_device *foreign_dev),
921c4076cddSVladimir Oltean int (*del_cb)(struct net_device *dev, const void *ctx,
922c4076cddSVladimir Oltean const struct switchdev_obj *obj))
923c4076cddSVladimir Oltean {
924c4076cddSVladimir Oltean int err;
925c4076cddSVladimir Oltean
926c4076cddSVladimir Oltean err = __switchdev_handle_port_obj_del(dev, port_obj_info, check_cb,
927c4076cddSVladimir Oltean foreign_dev_check_cb, del_cb);
928c4076cddSVladimir Oltean if (err == -EOPNOTSUPP)
929c4076cddSVladimir Oltean err = 0;
930c4076cddSVladimir Oltean return err;
931c4076cddSVladimir Oltean }
932c4076cddSVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del_foreign);
933c4076cddSVladimir 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))9341cb33af1SFlorian Fainelli static int __switchdev_handle_port_attr_set(struct net_device *dev,
9351cb33af1SFlorian Fainelli struct switchdev_notifier_port_attr_info *port_attr_info,
9361cb33af1SFlorian Fainelli bool (*check_cb)(const struct net_device *dev),
93769bfac96SVladimir Oltean int (*set_cb)(struct net_device *dev, const void *ctx,
9384c08c586SVladimir Oltean const struct switchdev_attr *attr,
9394c08c586SVladimir Oltean struct netlink_ext_ack *extack))
9401cb33af1SFlorian Fainelli {
94169bfac96SVladimir Oltean struct switchdev_notifier_info *info = &port_attr_info->info;
9424c08c586SVladimir Oltean struct netlink_ext_ack *extack;
9431cb33af1SFlorian Fainelli struct net_device *lower_dev;
9441cb33af1SFlorian Fainelli struct list_head *iter;
9451cb33af1SFlorian Fainelli int err = -EOPNOTSUPP;
9461cb33af1SFlorian Fainelli
94769bfac96SVladimir Oltean extack = switchdev_notifier_info_to_extack(info);
9484c08c586SVladimir Oltean
9491cb33af1SFlorian Fainelli if (check_cb(dev)) {
95069bfac96SVladimir Oltean err = set_cb(dev, info->ctx, port_attr_info->attr, extack);
95120776b46SRasmus Villemoes if (err != -EOPNOTSUPP)
9521cb33af1SFlorian Fainelli port_attr_info->handled = true;
95320776b46SRasmus Villemoes return err;
9541cb33af1SFlorian Fainelli }
9551cb33af1SFlorian Fainelli
9561cb33af1SFlorian Fainelli /* Switch ports might be stacked under e.g. a LAG. Ignore the
9571cb33af1SFlorian Fainelli * unsupported devices, another driver might be able to handle them. But
9581cb33af1SFlorian Fainelli * propagate to the callers any hard errors.
9591cb33af1SFlorian Fainelli *
9601cb33af1SFlorian Fainelli * If the driver does its own bookkeeping of stacked ports, it's not
9611cb33af1SFlorian Fainelli * necessary to go through this helper.
9621cb33af1SFlorian Fainelli */
9631cb33af1SFlorian Fainelli netdev_for_each_lower_dev(dev, lower_dev, iter) {
96407c6f980SRussell King if (netif_is_bridge_master(lower_dev))
96507c6f980SRussell King continue;
96607c6f980SRussell King
9671cb33af1SFlorian Fainelli err = __switchdev_handle_port_attr_set(lower_dev, port_attr_info,
9681cb33af1SFlorian Fainelli check_cb, set_cb);
9691cb33af1SFlorian Fainelli if (err && err != -EOPNOTSUPP)
9701cb33af1SFlorian Fainelli return err;
9711cb33af1SFlorian Fainelli }
9721cb33af1SFlorian Fainelli
9731cb33af1SFlorian Fainelli return err;
9741cb33af1SFlorian Fainelli }
9751cb33af1SFlorian 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))9761cb33af1SFlorian Fainelli int switchdev_handle_port_attr_set(struct net_device *dev,
9771cb33af1SFlorian Fainelli struct switchdev_notifier_port_attr_info *port_attr_info,
9781cb33af1SFlorian Fainelli bool (*check_cb)(const struct net_device *dev),
97969bfac96SVladimir Oltean int (*set_cb)(struct net_device *dev, const void *ctx,
9804c08c586SVladimir Oltean const struct switchdev_attr *attr,
9814c08c586SVladimir Oltean struct netlink_ext_ack *extack))
9821cb33af1SFlorian Fainelli {
9831cb33af1SFlorian Fainelli int err;
9841cb33af1SFlorian Fainelli
9851cb33af1SFlorian Fainelli err = __switchdev_handle_port_attr_set(dev, port_attr_info, check_cb,
9861cb33af1SFlorian Fainelli set_cb);
9871cb33af1SFlorian Fainelli if (err == -EOPNOTSUPP)
9881cb33af1SFlorian Fainelli err = 0;
9891cb33af1SFlorian Fainelli return err;
9901cb33af1SFlorian Fainelli }
9911cb33af1SFlorian Fainelli EXPORT_SYMBOL_GPL(switchdev_handle_port_attr_set);
992957e2235SVladimir 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)993957e2235SVladimir Oltean int switchdev_bridge_port_offload(struct net_device *brport_dev,
994957e2235SVladimir Oltean struct net_device *dev, const void *ctx,
995957e2235SVladimir Oltean struct notifier_block *atomic_nb,
996957e2235SVladimir Oltean struct notifier_block *blocking_nb,
997957e2235SVladimir Oltean bool tx_fwd_offload,
998957e2235SVladimir Oltean struct netlink_ext_ack *extack)
999957e2235SVladimir Oltean {
1000957e2235SVladimir Oltean struct switchdev_notifier_brport_info brport_info = {
1001957e2235SVladimir Oltean .brport = {
1002957e2235SVladimir Oltean .dev = dev,
1003957e2235SVladimir Oltean .ctx = ctx,
1004957e2235SVladimir Oltean .atomic_nb = atomic_nb,
1005957e2235SVladimir Oltean .blocking_nb = blocking_nb,
1006957e2235SVladimir Oltean .tx_fwd_offload = tx_fwd_offload,
1007957e2235SVladimir Oltean },
1008957e2235SVladimir Oltean };
1009957e2235SVladimir Oltean int err;
1010957e2235SVladimir Oltean
1011957e2235SVladimir Oltean ASSERT_RTNL();
1012957e2235SVladimir Oltean
1013957e2235SVladimir Oltean err = call_switchdev_blocking_notifiers(SWITCHDEV_BRPORT_OFFLOADED,
1014957e2235SVladimir Oltean brport_dev, &brport_info.info,
1015957e2235SVladimir Oltean extack);
1016957e2235SVladimir Oltean return notifier_to_errno(err);
1017957e2235SVladimir Oltean }
1018957e2235SVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_bridge_port_offload);
1019957e2235SVladimir Oltean
switchdev_bridge_port_unoffload(struct net_device * brport_dev,const void * ctx,struct notifier_block * atomic_nb,struct notifier_block * blocking_nb)1020957e2235SVladimir Oltean void switchdev_bridge_port_unoffload(struct net_device *brport_dev,
1021957e2235SVladimir Oltean const void *ctx,
1022957e2235SVladimir Oltean struct notifier_block *atomic_nb,
1023957e2235SVladimir Oltean struct notifier_block *blocking_nb)
1024957e2235SVladimir Oltean {
1025957e2235SVladimir Oltean struct switchdev_notifier_brport_info brport_info = {
1026957e2235SVladimir Oltean .brport = {
1027957e2235SVladimir Oltean .ctx = ctx,
1028957e2235SVladimir Oltean .atomic_nb = atomic_nb,
1029957e2235SVladimir Oltean .blocking_nb = blocking_nb,
1030957e2235SVladimir Oltean },
1031957e2235SVladimir Oltean };
1032957e2235SVladimir Oltean
1033957e2235SVladimir Oltean ASSERT_RTNL();
1034957e2235SVladimir Oltean
1035957e2235SVladimir Oltean call_switchdev_blocking_notifiers(SWITCHDEV_BRPORT_UNOFFLOADED,
1036957e2235SVladimir Oltean brport_dev, &brport_info.info,
1037957e2235SVladimir Oltean NULL);
1038957e2235SVladimir Oltean }
1039957e2235SVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_bridge_port_unoffload);
1040f2e2857bSPetr 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)1041f2e2857bSPetr Machata int switchdev_bridge_port_replay(struct net_device *brport_dev,
1042f2e2857bSPetr Machata struct net_device *dev, const void *ctx,
1043f2e2857bSPetr Machata struct notifier_block *atomic_nb,
1044f2e2857bSPetr Machata struct notifier_block *blocking_nb,
1045f2e2857bSPetr Machata struct netlink_ext_ack *extack)
1046f2e2857bSPetr Machata {
1047f2e2857bSPetr Machata struct switchdev_notifier_brport_info brport_info = {
1048f2e2857bSPetr Machata .brport = {
1049f2e2857bSPetr Machata .dev = dev,
1050f2e2857bSPetr Machata .ctx = ctx,
1051f2e2857bSPetr Machata .atomic_nb = atomic_nb,
1052f2e2857bSPetr Machata .blocking_nb = blocking_nb,
1053f2e2857bSPetr Machata },
1054f2e2857bSPetr Machata };
1055f2e2857bSPetr Machata int err;
1056f2e2857bSPetr Machata
1057f2e2857bSPetr Machata ASSERT_RTNL();
1058f2e2857bSPetr Machata
1059f2e2857bSPetr Machata err = call_switchdev_blocking_notifiers(SWITCHDEV_BRPORT_REPLAY,
1060f2e2857bSPetr Machata brport_dev, &brport_info.info,
1061f2e2857bSPetr Machata extack);
1062f2e2857bSPetr Machata return notifier_to_errno(err);
1063f2e2857bSPetr Machata }
1064f2e2857bSPetr Machata EXPORT_SYMBOL_GPL(switchdev_bridge_port_replay);
1065