xref: /linux/net/dsa/switch.c (revision fac6abd5f13297ddeae7ff6eb903e933cffc071b)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2f515f192SVivien Didelot /*
3f515f192SVivien Didelot  * Handling of a single switch chip, part of a switch fabric
4f515f192SVivien Didelot  *
54333d619SVivien Didelot  * Copyright (c) 2017 Savoir-faire Linux Inc.
64333d619SVivien Didelot  *	Vivien Didelot <vivien.didelot@savoirfairelinux.com>
7f515f192SVivien Didelot  */
8f515f192SVivien Didelot 
9d371b7c9SVladimir Oltean #include <linux/if_bridge.h>
10f515f192SVivien Didelot #include <linux/netdevice.h>
11f515f192SVivien Didelot #include <linux/notifier.h>
12061f6a50SFlorian Fainelli #include <linux/if_vlan.h>
131faabf74SVivien Didelot #include <net/switchdev.h>
14ea5dd34bSVivien Didelot 
15ea5dd34bSVivien Didelot #include "dsa_priv.h"
16f515f192SVivien Didelot 
171faabf74SVivien Didelot static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds,
181faabf74SVivien Didelot 						   unsigned int ageing_time)
191faabf74SVivien Didelot {
20d0004a02SVladimir Oltean 	struct dsa_port *dp;
211faabf74SVivien Didelot 
22d0004a02SVladimir Oltean 	dsa_switch_for_each_port(dp, ds)
231faabf74SVivien Didelot 		if (dp->ageing_time && dp->ageing_time < ageing_time)
241faabf74SVivien Didelot 			ageing_time = dp->ageing_time;
251faabf74SVivien Didelot 
261faabf74SVivien Didelot 	return ageing_time;
271faabf74SVivien Didelot }
281faabf74SVivien Didelot 
291faabf74SVivien Didelot static int dsa_switch_ageing_time(struct dsa_switch *ds,
301faabf74SVivien Didelot 				  struct dsa_notifier_ageing_time_info *info)
311faabf74SVivien Didelot {
321faabf74SVivien Didelot 	unsigned int ageing_time = info->ageing_time;
331faabf74SVivien Didelot 
341faabf74SVivien Didelot 	if (ds->ageing_time_min && ageing_time < ds->ageing_time_min)
351faabf74SVivien Didelot 		return -ERANGE;
3677b61365SVladimir Oltean 
371faabf74SVivien Didelot 	if (ds->ageing_time_max && ageing_time > ds->ageing_time_max)
381faabf74SVivien Didelot 		return -ERANGE;
391faabf74SVivien Didelot 
401faabf74SVivien Didelot 	/* Program the fastest ageing time in case of multiple bridges */
411faabf74SVivien Didelot 	ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time);
421faabf74SVivien Didelot 
431faabf74SVivien Didelot 	if (ds->ops->set_ageing_time)
441faabf74SVivien Didelot 		return ds->ops->set_ageing_time(ds, ageing_time);
451faabf74SVivien Didelot 
461faabf74SVivien Didelot 	return 0;
471faabf74SVivien Didelot }
481faabf74SVivien Didelot 
49*fac6abd5SVladimir Oltean static bool dsa_port_mtu_match(struct dsa_port *dp,
50bfcb8132SVladimir Oltean 			       struct dsa_notifier_mtu_info *info)
51bfcb8132SVladimir Oltean {
52*fac6abd5SVladimir Oltean 	if (dp->ds->index == info->sw_index && dp->index == info->port)
5388faba20SVladimir Oltean 		return true;
54bfcb8132SVladimir Oltean 
5588faba20SVladimir Oltean 	/* Do not propagate to other switches in the tree if the notifier was
5688faba20SVladimir Oltean 	 * targeted for a single switch.
5788faba20SVladimir Oltean 	 */
5888faba20SVladimir Oltean 	if (info->targeted_match)
59bfcb8132SVladimir Oltean 		return false;
60bfcb8132SVladimir Oltean 
61*fac6abd5SVladimir Oltean 	if (dsa_port_is_dsa(dp) || dsa_port_is_cpu(dp))
62bfcb8132SVladimir Oltean 		return true;
63bfcb8132SVladimir Oltean 
64bfcb8132SVladimir Oltean 	return false;
65bfcb8132SVladimir Oltean }
66bfcb8132SVladimir Oltean 
67bfcb8132SVladimir Oltean static int dsa_switch_mtu(struct dsa_switch *ds,
68bfcb8132SVladimir Oltean 			  struct dsa_notifier_mtu_info *info)
69bfcb8132SVladimir Oltean {
70*fac6abd5SVladimir Oltean 	struct dsa_port *dp;
71*fac6abd5SVladimir Oltean 	int ret;
72bfcb8132SVladimir Oltean 
73bfcb8132SVladimir Oltean 	if (!ds->ops->port_change_mtu)
74bfcb8132SVladimir Oltean 		return -EOPNOTSUPP;
75bfcb8132SVladimir Oltean 
76*fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
77*fac6abd5SVladimir Oltean 		if (dsa_port_mtu_match(dp, info)) {
78*fac6abd5SVladimir Oltean 			ret = ds->ops->port_change_mtu(ds, dp->index,
79*fac6abd5SVladimir Oltean 						       info->mtu);
80bfcb8132SVladimir Oltean 			if (ret)
81bfcb8132SVladimir Oltean 				return ret;
82bfcb8132SVladimir Oltean 		}
83bfcb8132SVladimir Oltean 	}
84bfcb8132SVladimir Oltean 
85bfcb8132SVladimir Oltean 	return 0;
86bfcb8132SVladimir Oltean }
87bfcb8132SVladimir Oltean 
8804d3a4c6SVivien Didelot static int dsa_switch_bridge_join(struct dsa_switch *ds,
8904d3a4c6SVivien Didelot 				  struct dsa_notifier_bridge_info *info)
9004d3a4c6SVivien Didelot {
91f66a6a69SVladimir Oltean 	struct dsa_switch_tree *dst = ds->dst;
92e19cc13cSVladimir Oltean 	int err;
93f66a6a69SVladimir Oltean 
9467b5fb5dSVladimir Oltean 	if (dst->index == info->tree_index && ds->index == info->sw_index) {
9567b5fb5dSVladimir Oltean 		if (!ds->ops->port_bridge_join)
9667b5fb5dSVladimir Oltean 			return -EOPNOTSUPP;
9767b5fb5dSVladimir Oltean 
98e19cc13cSVladimir Oltean 		err = ds->ops->port_bridge_join(ds, info->port, info->br);
99e19cc13cSVladimir Oltean 		if (err)
100e19cc13cSVladimir Oltean 			return err;
101e19cc13cSVladimir Oltean 	}
10204d3a4c6SVivien Didelot 
103f66a6a69SVladimir Oltean 	if ((dst->index != info->tree_index || ds->index != info->sw_index) &&
104e19cc13cSVladimir Oltean 	    ds->ops->crosschip_bridge_join) {
105e19cc13cSVladimir Oltean 		err = ds->ops->crosschip_bridge_join(ds, info->tree_index,
106f66a6a69SVladimir Oltean 						     info->sw_index,
10740ef2c93SVivien Didelot 						     info->port, info->br);
108e19cc13cSVladimir Oltean 		if (err)
109e19cc13cSVladimir Oltean 			return err;
110e19cc13cSVladimir Oltean 	}
11104d3a4c6SVivien Didelot 
112e19cc13cSVladimir Oltean 	return dsa_tag_8021q_bridge_join(ds, info);
11304d3a4c6SVivien Didelot }
11404d3a4c6SVivien Didelot 
11504d3a4c6SVivien Didelot static int dsa_switch_bridge_leave(struct dsa_switch *ds,
11604d3a4c6SVivien Didelot 				   struct dsa_notifier_bridge_info *info)
11704d3a4c6SVivien Didelot {
118f66a6a69SVladimir Oltean 	struct dsa_switch_tree *dst = ds->dst;
11989153ed6SVladimir Oltean 	struct netlink_ext_ack extack = {0};
12058adf9dcSVladimir Oltean 	bool change_vlan_filtering = false;
12158adf9dcSVladimir Oltean 	bool vlan_filtering;
122d0004a02SVladimir Oltean 	struct dsa_port *dp;
123d0004a02SVladimir Oltean 	int err;
124d371b7c9SVladimir Oltean 
125f66a6a69SVladimir Oltean 	if (dst->index == info->tree_index && ds->index == info->sw_index &&
126bcb9928aSVladimir Oltean 	    ds->ops->port_bridge_leave)
12704d3a4c6SVivien Didelot 		ds->ops->port_bridge_leave(ds, info->port, info->br);
12804d3a4c6SVivien Didelot 
129f66a6a69SVladimir Oltean 	if ((dst->index != info->tree_index || ds->index != info->sw_index) &&
130bcb9928aSVladimir Oltean 	    ds->ops->crosschip_bridge_leave)
131f66a6a69SVladimir Oltean 		ds->ops->crosschip_bridge_leave(ds, info->tree_index,
132f66a6a69SVladimir Oltean 						info->sw_index, info->port,
13340ef2c93SVivien Didelot 						info->br);
13404d3a4c6SVivien Didelot 
13558adf9dcSVladimir Oltean 	if (ds->needs_standalone_vlan_filtering && !br_vlan_enabled(info->br)) {
13658adf9dcSVladimir Oltean 		change_vlan_filtering = true;
13758adf9dcSVladimir Oltean 		vlan_filtering = true;
13858adf9dcSVladimir Oltean 	} else if (!ds->needs_standalone_vlan_filtering &&
13958adf9dcSVladimir Oltean 		   br_vlan_enabled(info->br)) {
14058adf9dcSVladimir Oltean 		change_vlan_filtering = true;
14158adf9dcSVladimir Oltean 		vlan_filtering = false;
14258adf9dcSVladimir Oltean 	}
14358adf9dcSVladimir Oltean 
144d371b7c9SVladimir Oltean 	/* If the bridge was vlan_filtering, the bridge core doesn't trigger an
145d371b7c9SVladimir Oltean 	 * event for changing vlan_filtering setting upon slave ports leaving
146d371b7c9SVladimir Oltean 	 * it. That is a good thing, because that lets us handle it and also
147d371b7c9SVladimir Oltean 	 * handle the case where the switch's vlan_filtering setting is global
148d371b7c9SVladimir Oltean 	 * (not per port). When that happens, the correct moment to trigger the
149479dc497SVladimir Oltean 	 * vlan_filtering callback is only when the last port leaves the last
150479dc497SVladimir Oltean 	 * VLAN-aware bridge.
151d371b7c9SVladimir Oltean 	 */
15258adf9dcSVladimir Oltean 	if (change_vlan_filtering && ds->vlan_filtering_is_global) {
153d0004a02SVladimir Oltean 		dsa_switch_for_each_port(dp, ds) {
154479dc497SVladimir Oltean 			struct net_device *bridge_dev;
155479dc497SVladimir Oltean 
156d0004a02SVladimir Oltean 			bridge_dev = dp->bridge_dev;
157479dc497SVladimir Oltean 
158479dc497SVladimir Oltean 			if (bridge_dev && br_vlan_enabled(bridge_dev)) {
15958adf9dcSVladimir Oltean 				change_vlan_filtering = false;
160d371b7c9SVladimir Oltean 				break;
161d371b7c9SVladimir Oltean 			}
162d371b7c9SVladimir Oltean 		}
163d371b7c9SVladimir Oltean 	}
16458adf9dcSVladimir Oltean 
16558adf9dcSVladimir Oltean 	if (change_vlan_filtering) {
1662e554a7aSVladimir Oltean 		err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port),
16758adf9dcSVladimir Oltean 					      vlan_filtering, &extack);
16889153ed6SVladimir Oltean 		if (extack._msg)
16989153ed6SVladimir Oltean 			dev_err(ds->dev, "port %d: %s\n", info->port,
17089153ed6SVladimir Oltean 				extack._msg);
17143a4b4dbSAlvin Šipraga 		if (err && err != -EOPNOTSUPP)
172d371b7c9SVladimir Oltean 			return err;
173d371b7c9SVladimir Oltean 	}
174e19cc13cSVladimir Oltean 
175e19cc13cSVladimir Oltean 	return dsa_tag_8021q_bridge_leave(ds, info);
17604d3a4c6SVivien Didelot }
17704d3a4c6SVivien Didelot 
178b8e997c4SVladimir Oltean /* Matches for all upstream-facing ports (the CPU port and all upstream-facing
179b8e997c4SVladimir Oltean  * DSA links) that sit between the targeted port on which the notifier was
180b8e997c4SVladimir Oltean  * emitted and its dedicated CPU port.
181b8e997c4SVladimir Oltean  */
182*fac6abd5SVladimir Oltean static bool dsa_port_host_address_match(struct dsa_port *dp,
183b8e997c4SVladimir Oltean 					int info_sw_index, int info_port)
184b8e997c4SVladimir Oltean {
185b8e997c4SVladimir Oltean 	struct dsa_port *targeted_dp, *cpu_dp;
186b8e997c4SVladimir Oltean 	struct dsa_switch *targeted_ds;
187b8e997c4SVladimir Oltean 
188*fac6abd5SVladimir Oltean 	targeted_ds = dsa_switch_find(dp->ds->dst->index, info_sw_index);
189b8e997c4SVladimir Oltean 	targeted_dp = dsa_to_port(targeted_ds, info_port);
190b8e997c4SVladimir Oltean 	cpu_dp = targeted_dp->cpu_dp;
191b8e997c4SVladimir Oltean 
192*fac6abd5SVladimir Oltean 	if (dsa_switch_is_upstream_of(dp->ds, targeted_ds))
193*fac6abd5SVladimir Oltean 		return dp->index == dsa_towards_port(dp->ds, cpu_dp->ds->index,
194b8e997c4SVladimir Oltean 						     cpu_dp->index);
195b8e997c4SVladimir Oltean 
196b8e997c4SVladimir Oltean 	return false;
197b8e997c4SVladimir Oltean }
198b8e997c4SVladimir Oltean 
199161ca59dSVladimir Oltean static struct dsa_mac_addr *dsa_mac_addr_find(struct list_head *addr_list,
200161ca59dSVladimir Oltean 					      const unsigned char *addr,
201161ca59dSVladimir Oltean 					      u16 vid)
202161ca59dSVladimir Oltean {
203161ca59dSVladimir Oltean 	struct dsa_mac_addr *a;
204161ca59dSVladimir Oltean 
205161ca59dSVladimir Oltean 	list_for_each_entry(a, addr_list, list)
206161ca59dSVladimir Oltean 		if (ether_addr_equal(a->addr, addr) && a->vid == vid)
207161ca59dSVladimir Oltean 			return a;
208161ca59dSVladimir Oltean 
209161ca59dSVladimir Oltean 	return NULL;
210161ca59dSVladimir Oltean }
211161ca59dSVladimir Oltean 
212*fac6abd5SVladimir Oltean static int dsa_port_do_mdb_add(struct dsa_port *dp,
213161ca59dSVladimir Oltean 			       const struct switchdev_obj_port_mdb *mdb)
214161ca59dSVladimir Oltean {
215*fac6abd5SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
216161ca59dSVladimir Oltean 	struct dsa_mac_addr *a;
217*fac6abd5SVladimir Oltean 	int port = dp->index;
218161ca59dSVladimir Oltean 	int err;
219161ca59dSVladimir Oltean 
220161ca59dSVladimir Oltean 	/* No need to bother with refcounting for user ports */
221161ca59dSVladimir Oltean 	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
222161ca59dSVladimir Oltean 		return ds->ops->port_mdb_add(ds, port, mdb);
223161ca59dSVladimir Oltean 
224161ca59dSVladimir Oltean 	a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid);
225161ca59dSVladimir Oltean 	if (a) {
226161ca59dSVladimir Oltean 		refcount_inc(&a->refcount);
227161ca59dSVladimir Oltean 		return 0;
228161ca59dSVladimir Oltean 	}
229161ca59dSVladimir Oltean 
230161ca59dSVladimir Oltean 	a = kzalloc(sizeof(*a), GFP_KERNEL);
231161ca59dSVladimir Oltean 	if (!a)
232161ca59dSVladimir Oltean 		return -ENOMEM;
233161ca59dSVladimir Oltean 
234161ca59dSVladimir Oltean 	err = ds->ops->port_mdb_add(ds, port, mdb);
235161ca59dSVladimir Oltean 	if (err) {
236161ca59dSVladimir Oltean 		kfree(a);
237161ca59dSVladimir Oltean 		return err;
238161ca59dSVladimir Oltean 	}
239161ca59dSVladimir Oltean 
240161ca59dSVladimir Oltean 	ether_addr_copy(a->addr, mdb->addr);
241161ca59dSVladimir Oltean 	a->vid = mdb->vid;
242161ca59dSVladimir Oltean 	refcount_set(&a->refcount, 1);
243161ca59dSVladimir Oltean 	list_add_tail(&a->list, &dp->mdbs);
244161ca59dSVladimir Oltean 
245161ca59dSVladimir Oltean 	return 0;
246161ca59dSVladimir Oltean }
247161ca59dSVladimir Oltean 
248*fac6abd5SVladimir Oltean static int dsa_port_do_mdb_del(struct dsa_port *dp,
249161ca59dSVladimir Oltean 			       const struct switchdev_obj_port_mdb *mdb)
250161ca59dSVladimir Oltean {
251*fac6abd5SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
252161ca59dSVladimir Oltean 	struct dsa_mac_addr *a;
253*fac6abd5SVladimir Oltean 	int port = dp->index;
254161ca59dSVladimir Oltean 	int err;
255161ca59dSVladimir Oltean 
256161ca59dSVladimir Oltean 	/* No need to bother with refcounting for user ports */
257161ca59dSVladimir Oltean 	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
258161ca59dSVladimir Oltean 		return ds->ops->port_mdb_del(ds, port, mdb);
259161ca59dSVladimir Oltean 
260161ca59dSVladimir Oltean 	a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid);
261161ca59dSVladimir Oltean 	if (!a)
262161ca59dSVladimir Oltean 		return -ENOENT;
263161ca59dSVladimir Oltean 
264161ca59dSVladimir Oltean 	if (!refcount_dec_and_test(&a->refcount))
265161ca59dSVladimir Oltean 		return 0;
266161ca59dSVladimir Oltean 
267161ca59dSVladimir Oltean 	err = ds->ops->port_mdb_del(ds, port, mdb);
268161ca59dSVladimir Oltean 	if (err) {
269161ca59dSVladimir Oltean 		refcount_inc(&a->refcount);
270161ca59dSVladimir Oltean 		return err;
271161ca59dSVladimir Oltean 	}
272161ca59dSVladimir Oltean 
273161ca59dSVladimir Oltean 	list_del(&a->list);
274161ca59dSVladimir Oltean 	kfree(a);
275161ca59dSVladimir Oltean 
276161ca59dSVladimir Oltean 	return 0;
277161ca59dSVladimir Oltean }
278161ca59dSVladimir Oltean 
279*fac6abd5SVladimir Oltean static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr,
280*fac6abd5SVladimir Oltean 			       u16 vid)
2813f6e32f9SVladimir Oltean {
282*fac6abd5SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
2833f6e32f9SVladimir Oltean 	struct dsa_mac_addr *a;
284*fac6abd5SVladimir Oltean 	int port = dp->index;
2853f6e32f9SVladimir Oltean 	int err;
2863f6e32f9SVladimir Oltean 
2873f6e32f9SVladimir Oltean 	/* No need to bother with refcounting for user ports */
2883f6e32f9SVladimir Oltean 	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
2893f6e32f9SVladimir Oltean 		return ds->ops->port_fdb_add(ds, port, addr, vid);
2903f6e32f9SVladimir Oltean 
2913f6e32f9SVladimir Oltean 	a = dsa_mac_addr_find(&dp->fdbs, addr, vid);
2923f6e32f9SVladimir Oltean 	if (a) {
2933f6e32f9SVladimir Oltean 		refcount_inc(&a->refcount);
2943f6e32f9SVladimir Oltean 		return 0;
2953f6e32f9SVladimir Oltean 	}
2963f6e32f9SVladimir Oltean 
2973f6e32f9SVladimir Oltean 	a = kzalloc(sizeof(*a), GFP_KERNEL);
2983f6e32f9SVladimir Oltean 	if (!a)
2993f6e32f9SVladimir Oltean 		return -ENOMEM;
3003f6e32f9SVladimir Oltean 
3013f6e32f9SVladimir Oltean 	err = ds->ops->port_fdb_add(ds, port, addr, vid);
3023f6e32f9SVladimir Oltean 	if (err) {
3033f6e32f9SVladimir Oltean 		kfree(a);
3043f6e32f9SVladimir Oltean 		return err;
3053f6e32f9SVladimir Oltean 	}
3063f6e32f9SVladimir Oltean 
3073f6e32f9SVladimir Oltean 	ether_addr_copy(a->addr, addr);
3083f6e32f9SVladimir Oltean 	a->vid = vid;
3093f6e32f9SVladimir Oltean 	refcount_set(&a->refcount, 1);
3103f6e32f9SVladimir Oltean 	list_add_tail(&a->list, &dp->fdbs);
3113f6e32f9SVladimir Oltean 
3123f6e32f9SVladimir Oltean 	return 0;
3133f6e32f9SVladimir Oltean }
3143f6e32f9SVladimir Oltean 
315*fac6abd5SVladimir Oltean static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr,
316*fac6abd5SVladimir Oltean 			       u16 vid)
3173f6e32f9SVladimir Oltean {
318*fac6abd5SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
3193f6e32f9SVladimir Oltean 	struct dsa_mac_addr *a;
320*fac6abd5SVladimir Oltean 	int port = dp->index;
3213f6e32f9SVladimir Oltean 	int err;
3223f6e32f9SVladimir Oltean 
3233f6e32f9SVladimir Oltean 	/* No need to bother with refcounting for user ports */
3243f6e32f9SVladimir Oltean 	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
3253f6e32f9SVladimir Oltean 		return ds->ops->port_fdb_del(ds, port, addr, vid);
3263f6e32f9SVladimir Oltean 
3273f6e32f9SVladimir Oltean 	a = dsa_mac_addr_find(&dp->fdbs, addr, vid);
3283f6e32f9SVladimir Oltean 	if (!a)
3293f6e32f9SVladimir Oltean 		return -ENOENT;
3303f6e32f9SVladimir Oltean 
3313f6e32f9SVladimir Oltean 	if (!refcount_dec_and_test(&a->refcount))
3323f6e32f9SVladimir Oltean 		return 0;
3333f6e32f9SVladimir Oltean 
3343f6e32f9SVladimir Oltean 	err = ds->ops->port_fdb_del(ds, port, addr, vid);
3353f6e32f9SVladimir Oltean 	if (err) {
3363f6e32f9SVladimir Oltean 		refcount_inc(&a->refcount);
3373f6e32f9SVladimir Oltean 		return err;
3383f6e32f9SVladimir Oltean 	}
3393f6e32f9SVladimir Oltean 
3403f6e32f9SVladimir Oltean 	list_del(&a->list);
3413f6e32f9SVladimir Oltean 	kfree(a);
3423f6e32f9SVladimir Oltean 
3433f6e32f9SVladimir Oltean 	return 0;
3443f6e32f9SVladimir Oltean }
3453f6e32f9SVladimir Oltean 
3463dc80afcSVladimir Oltean static int dsa_switch_host_fdb_add(struct dsa_switch *ds,
3473dc80afcSVladimir Oltean 				   struct dsa_notifier_fdb_info *info)
3483dc80afcSVladimir Oltean {
349*fac6abd5SVladimir Oltean 	struct dsa_port *dp;
3503dc80afcSVladimir Oltean 	int err = 0;
3513dc80afcSVladimir Oltean 
3523dc80afcSVladimir Oltean 	if (!ds->ops->port_fdb_add)
3533dc80afcSVladimir Oltean 		return -EOPNOTSUPP;
3543dc80afcSVladimir Oltean 
355*fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
356*fac6abd5SVladimir Oltean 		if (dsa_port_host_address_match(dp, info->sw_index,
3573dc80afcSVladimir Oltean 						info->port)) {
358*fac6abd5SVladimir Oltean 			err = dsa_port_do_fdb_add(dp, info->addr, info->vid);
3593dc80afcSVladimir Oltean 			if (err)
3603dc80afcSVladimir Oltean 				break;
3613dc80afcSVladimir Oltean 		}
3623dc80afcSVladimir Oltean 	}
3633dc80afcSVladimir Oltean 
3643dc80afcSVladimir Oltean 	return err;
3653dc80afcSVladimir Oltean }
3663dc80afcSVladimir Oltean 
3673dc80afcSVladimir Oltean static int dsa_switch_host_fdb_del(struct dsa_switch *ds,
3683dc80afcSVladimir Oltean 				   struct dsa_notifier_fdb_info *info)
3693dc80afcSVladimir Oltean {
370*fac6abd5SVladimir Oltean 	struct dsa_port *dp;
3713f6e32f9SVladimir Oltean 	int err = 0;
3723f6e32f9SVladimir Oltean 
3733dc80afcSVladimir Oltean 	if (!ds->ops->port_fdb_del)
3743dc80afcSVladimir Oltean 		return -EOPNOTSUPP;
3753dc80afcSVladimir Oltean 
376*fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
377*fac6abd5SVladimir Oltean 		if (dsa_port_host_address_match(dp, info->sw_index,
3783f6e32f9SVladimir Oltean 						info->port)) {
379*fac6abd5SVladimir Oltean 			err = dsa_port_do_fdb_del(dp, info->addr, info->vid);
3803f6e32f9SVladimir Oltean 			if (err)
3813f6e32f9SVladimir Oltean 				break;
3823f6e32f9SVladimir Oltean 		}
3833f6e32f9SVladimir Oltean 	}
3843dc80afcSVladimir Oltean 
3853f6e32f9SVladimir Oltean 	return err;
3863dc80afcSVladimir Oltean }
3873dc80afcSVladimir Oltean 
388685fb6a4SVivien Didelot static int dsa_switch_fdb_add(struct dsa_switch *ds,
389685fb6a4SVivien Didelot 			      struct dsa_notifier_fdb_info *info)
390685fb6a4SVivien Didelot {
3913169241fSVivien Didelot 	int port = dsa_towards_port(ds, info->sw_index, info->port);
392*fac6abd5SVladimir Oltean 	struct dsa_port *dp = dsa_to_port(ds, port);
393685fb6a4SVivien Didelot 
3941b6dd556SArkadi Sharshevsky 	if (!ds->ops->port_fdb_add)
395685fb6a4SVivien Didelot 		return -EOPNOTSUPP;
396685fb6a4SVivien Didelot 
397*fac6abd5SVladimir Oltean 	return dsa_port_do_fdb_add(dp, info->addr, info->vid);
398685fb6a4SVivien Didelot }
399685fb6a4SVivien Didelot 
400685fb6a4SVivien Didelot static int dsa_switch_fdb_del(struct dsa_switch *ds,
401685fb6a4SVivien Didelot 			      struct dsa_notifier_fdb_info *info)
402685fb6a4SVivien Didelot {
4033169241fSVivien Didelot 	int port = dsa_towards_port(ds, info->sw_index, info->port);
404*fac6abd5SVladimir Oltean 	struct dsa_port *dp = dsa_to_port(ds, port);
405685fb6a4SVivien Didelot 
406685fb6a4SVivien Didelot 	if (!ds->ops->port_fdb_del)
407685fb6a4SVivien Didelot 		return -EOPNOTSUPP;
408685fb6a4SVivien Didelot 
409*fac6abd5SVladimir Oltean 	return dsa_port_do_fdb_del(dp, info->addr, info->vid);
410685fb6a4SVivien Didelot }
411685fb6a4SVivien Didelot 
41218596f50SGeorge McCollister static int dsa_switch_hsr_join(struct dsa_switch *ds,
41318596f50SGeorge McCollister 			       struct dsa_notifier_hsr_info *info)
41418596f50SGeorge McCollister {
41518596f50SGeorge McCollister 	if (ds->index == info->sw_index && ds->ops->port_hsr_join)
41618596f50SGeorge McCollister 		return ds->ops->port_hsr_join(ds, info->port, info->hsr);
41718596f50SGeorge McCollister 
41818596f50SGeorge McCollister 	return -EOPNOTSUPP;
41918596f50SGeorge McCollister }
42018596f50SGeorge McCollister 
42118596f50SGeorge McCollister static int dsa_switch_hsr_leave(struct dsa_switch *ds,
42218596f50SGeorge McCollister 				struct dsa_notifier_hsr_info *info)
42318596f50SGeorge McCollister {
42418596f50SGeorge McCollister 	if (ds->index == info->sw_index && ds->ops->port_hsr_leave)
42518596f50SGeorge McCollister 		return ds->ops->port_hsr_leave(ds, info->port, info->hsr);
42618596f50SGeorge McCollister 
42718596f50SGeorge McCollister 	return -EOPNOTSUPP;
42818596f50SGeorge McCollister }
42918596f50SGeorge McCollister 
430058102a6STobias Waldekranz static int dsa_switch_lag_change(struct dsa_switch *ds,
431058102a6STobias Waldekranz 				 struct dsa_notifier_lag_info *info)
432058102a6STobias Waldekranz {
433058102a6STobias Waldekranz 	if (ds->index == info->sw_index && ds->ops->port_lag_change)
434058102a6STobias Waldekranz 		return ds->ops->port_lag_change(ds, info->port);
435058102a6STobias Waldekranz 
436058102a6STobias Waldekranz 	if (ds->index != info->sw_index && ds->ops->crosschip_lag_change)
437058102a6STobias Waldekranz 		return ds->ops->crosschip_lag_change(ds, info->sw_index,
438058102a6STobias Waldekranz 						     info->port);
439058102a6STobias Waldekranz 
440058102a6STobias Waldekranz 	return 0;
441058102a6STobias Waldekranz }
442058102a6STobias Waldekranz 
443058102a6STobias Waldekranz static int dsa_switch_lag_join(struct dsa_switch *ds,
444058102a6STobias Waldekranz 			       struct dsa_notifier_lag_info *info)
445058102a6STobias Waldekranz {
446058102a6STobias Waldekranz 	if (ds->index == info->sw_index && ds->ops->port_lag_join)
447058102a6STobias Waldekranz 		return ds->ops->port_lag_join(ds, info->port, info->lag,
448058102a6STobias Waldekranz 					      info->info);
449058102a6STobias Waldekranz 
450058102a6STobias Waldekranz 	if (ds->index != info->sw_index && ds->ops->crosschip_lag_join)
451058102a6STobias Waldekranz 		return ds->ops->crosschip_lag_join(ds, info->sw_index,
452058102a6STobias Waldekranz 						   info->port, info->lag,
453058102a6STobias Waldekranz 						   info->info);
454058102a6STobias Waldekranz 
455b71d0987SVladimir Oltean 	return -EOPNOTSUPP;
456058102a6STobias Waldekranz }
457058102a6STobias Waldekranz 
458058102a6STobias Waldekranz static int dsa_switch_lag_leave(struct dsa_switch *ds,
459058102a6STobias Waldekranz 				struct dsa_notifier_lag_info *info)
460058102a6STobias Waldekranz {
461058102a6STobias Waldekranz 	if (ds->index == info->sw_index && ds->ops->port_lag_leave)
462058102a6STobias Waldekranz 		return ds->ops->port_lag_leave(ds, info->port, info->lag);
463058102a6STobias Waldekranz 
464058102a6STobias Waldekranz 	if (ds->index != info->sw_index && ds->ops->crosschip_lag_leave)
465058102a6STobias Waldekranz 		return ds->ops->crosschip_lag_leave(ds, info->sw_index,
466058102a6STobias Waldekranz 						    info->port, info->lag);
467058102a6STobias Waldekranz 
468b71d0987SVladimir Oltean 	return -EOPNOTSUPP;
469058102a6STobias Waldekranz }
470058102a6STobias Waldekranz 
471ffb68fc5SVladimir Oltean static int dsa_switch_mdb_add(struct dsa_switch *ds,
472e65d45ccSVivien Didelot 			      struct dsa_notifier_mdb_info *info)
473e6db98dbSVivien Didelot {
474abd49535SVladimir Oltean 	int port = dsa_towards_port(ds, info->sw_index, info->port);
475*fac6abd5SVladimir Oltean 	struct dsa_port *dp = dsa_to_port(ds, port);
476e6db98dbSVivien Didelot 
477a52b2da7SVladimir Oltean 	if (!ds->ops->port_mdb_add)
478e6db98dbSVivien Didelot 		return -EOPNOTSUPP;
479e6db98dbSVivien Didelot 
480*fac6abd5SVladimir Oltean 	return dsa_port_do_mdb_add(dp, info->mdb);
481e6db98dbSVivien Didelot }
4828ae5bcdcSVivien Didelot 
4838ae5bcdcSVivien Didelot static int dsa_switch_mdb_del(struct dsa_switch *ds,
4848ae5bcdcSVivien Didelot 			      struct dsa_notifier_mdb_info *info)
4858ae5bcdcSVivien Didelot {
486161ca59dSVladimir Oltean 	int port = dsa_towards_port(ds, info->sw_index, info->port);
487*fac6abd5SVladimir Oltean 	struct dsa_port *dp = dsa_to_port(ds, port);
488161ca59dSVladimir Oltean 
4898ae5bcdcSVivien Didelot 	if (!ds->ops->port_mdb_del)
4908ae5bcdcSVivien Didelot 		return -EOPNOTSUPP;
4918ae5bcdcSVivien Didelot 
492*fac6abd5SVladimir Oltean 	return dsa_port_do_mdb_del(dp, info->mdb);
4938ae5bcdcSVivien Didelot }
4948ae5bcdcSVivien Didelot 
495b8e997c4SVladimir Oltean static int dsa_switch_host_mdb_add(struct dsa_switch *ds,
496b8e997c4SVladimir Oltean 				   struct dsa_notifier_mdb_info *info)
497b8e997c4SVladimir Oltean {
498*fac6abd5SVladimir Oltean 	struct dsa_port *dp;
499b8e997c4SVladimir Oltean 	int err = 0;
500b8e997c4SVladimir Oltean 
501b8e997c4SVladimir Oltean 	if (!ds->ops->port_mdb_add)
502b8e997c4SVladimir Oltean 		return -EOPNOTSUPP;
503b8e997c4SVladimir Oltean 
504*fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
505*fac6abd5SVladimir Oltean 		if (dsa_port_host_address_match(dp, info->sw_index,
506b8e997c4SVladimir Oltean 						info->port)) {
507*fac6abd5SVladimir Oltean 			err = dsa_port_do_mdb_add(dp, info->mdb);
508b8e997c4SVladimir Oltean 			if (err)
509b8e997c4SVladimir Oltean 				break;
510b8e997c4SVladimir Oltean 		}
511b8e997c4SVladimir Oltean 	}
512b8e997c4SVladimir Oltean 
513b8e997c4SVladimir Oltean 	return err;
514b8e997c4SVladimir Oltean }
515b8e997c4SVladimir Oltean 
516b8e997c4SVladimir Oltean static int dsa_switch_host_mdb_del(struct dsa_switch *ds,
517b8e997c4SVladimir Oltean 				   struct dsa_notifier_mdb_info *info)
518b8e997c4SVladimir Oltean {
519*fac6abd5SVladimir Oltean 	struct dsa_port *dp;
520161ca59dSVladimir Oltean 	int err = 0;
521161ca59dSVladimir Oltean 
522b8e997c4SVladimir Oltean 	if (!ds->ops->port_mdb_del)
523b8e997c4SVladimir Oltean 		return -EOPNOTSUPP;
524b8e997c4SVladimir Oltean 
525*fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
526*fac6abd5SVladimir Oltean 		if (dsa_port_host_address_match(dp, info->sw_index,
527161ca59dSVladimir Oltean 						info->port)) {
528*fac6abd5SVladimir Oltean 			err = dsa_port_do_mdb_del(dp, info->mdb);
529161ca59dSVladimir Oltean 			if (err)
530161ca59dSVladimir Oltean 				break;
531161ca59dSVladimir Oltean 		}
532161ca59dSVladimir Oltean 	}
533b8e997c4SVladimir Oltean 
534161ca59dSVladimir Oltean 	return err;
535b8e997c4SVladimir Oltean }
536b8e997c4SVladimir Oltean 
537*fac6abd5SVladimir Oltean static bool dsa_port_vlan_match(struct dsa_port *dp,
538e65d45ccSVivien Didelot 				struct dsa_notifier_vlan_info *info)
539e65d45ccSVivien Didelot {
540*fac6abd5SVladimir Oltean 	if (dp->ds->index == info->sw_index && dp->index == info->port)
541e65d45ccSVivien Didelot 		return true;
542e65d45ccSVivien Didelot 
543*fac6abd5SVladimir Oltean 	if (dsa_port_is_dsa(dp))
544e65d45ccSVivien Didelot 		return true;
545e65d45ccSVivien Didelot 
546e65d45ccSVivien Didelot 	return false;
547e65d45ccSVivien Didelot }
548e65d45ccSVivien Didelot 
549ffb68fc5SVladimir Oltean static int dsa_switch_vlan_add(struct dsa_switch *ds,
550e65d45ccSVivien Didelot 			       struct dsa_notifier_vlan_info *info)
5519c428c59SVivien Didelot {
552*fac6abd5SVladimir Oltean 	struct dsa_port *dp;
553*fac6abd5SVladimir Oltean 	int err;
5549c428c59SVivien Didelot 
5551958d581SVladimir Oltean 	if (!ds->ops->port_vlan_add)
5569c428c59SVivien Didelot 		return -EOPNOTSUPP;
5579c428c59SVivien Didelot 
558*fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
559*fac6abd5SVladimir Oltean 		if (dsa_port_vlan_match(dp, info)) {
560*fac6abd5SVladimir Oltean 			err = ds->ops->port_vlan_add(ds, dp->index, info->vlan,
56131046a5fSVladimir Oltean 						     info->extack);
5629c428c59SVivien Didelot 			if (err)
5639c428c59SVivien Didelot 				return err;
5649c428c59SVivien Didelot 		}
565e65d45ccSVivien Didelot 	}
5669c428c59SVivien Didelot 
567d0c627b8SVivien Didelot 	return 0;
568d0c627b8SVivien Didelot }
569d0c627b8SVivien Didelot 
570d0c627b8SVivien Didelot static int dsa_switch_vlan_del(struct dsa_switch *ds,
571d0c627b8SVivien Didelot 			       struct dsa_notifier_vlan_info *info)
572d0c627b8SVivien Didelot {
573d0c627b8SVivien Didelot 	if (!ds->ops->port_vlan_del)
574d0c627b8SVivien Didelot 		return -EOPNOTSUPP;
575d0c627b8SVivien Didelot 
5761ca4aa9cSVivien Didelot 	if (ds->index == info->sw_index)
577e65d45ccSVivien Didelot 		return ds->ops->port_vlan_del(ds, info->port, info->vlan);
5781ca4aa9cSVivien Didelot 
5797e1741b4SVivien Didelot 	/* Do not deprogram the DSA links as they may be used as conduit
5807e1741b4SVivien Didelot 	 * for other VLAN members in the fabric.
5817e1741b4SVivien Didelot 	 */
5821ca4aa9cSVivien Didelot 	return 0;
583d0c627b8SVivien Didelot }
584d0c627b8SVivien Didelot 
58553da0ebaSVladimir Oltean static int dsa_switch_change_tag_proto(struct dsa_switch *ds,
58653da0ebaSVladimir Oltean 				       struct dsa_notifier_tag_proto_info *info)
58753da0ebaSVladimir Oltean {
58853da0ebaSVladimir Oltean 	const struct dsa_device_ops *tag_ops = info->tag_ops;
589d0004a02SVladimir Oltean 	struct dsa_port *dp, *cpu_dp;
590d0004a02SVladimir Oltean 	int err;
59153da0ebaSVladimir Oltean 
59253da0ebaSVladimir Oltean 	if (!ds->ops->change_tag_protocol)
59353da0ebaSVladimir Oltean 		return -EOPNOTSUPP;
59453da0ebaSVladimir Oltean 
59553da0ebaSVladimir Oltean 	ASSERT_RTNL();
59653da0ebaSVladimir Oltean 
597d0004a02SVladimir Oltean 	dsa_switch_for_each_cpu_port(cpu_dp, ds) {
598d0004a02SVladimir Oltean 		err = ds->ops->change_tag_protocol(ds, cpu_dp->index,
599d0004a02SVladimir Oltean 						   tag_ops->proto);
60053da0ebaSVladimir Oltean 		if (err)
60153da0ebaSVladimir Oltean 			return err;
60253da0ebaSVladimir Oltean 
603d0004a02SVladimir Oltean 		dsa_port_set_tag_protocol(cpu_dp, tag_ops);
60453da0ebaSVladimir Oltean 	}
60553da0ebaSVladimir Oltean 
60653da0ebaSVladimir Oltean 	/* Now that changing the tag protocol can no longer fail, let's update
60753da0ebaSVladimir Oltean 	 * the remaining bits which are "duplicated for faster access", and the
60853da0ebaSVladimir Oltean 	 * bits that depend on the tagger, such as the MTU.
60953da0ebaSVladimir Oltean 	 */
610d0004a02SVladimir Oltean 	dsa_switch_for_each_user_port(dp, ds) {
611d0004a02SVladimir Oltean 		struct net_device *slave = dp->slave;
61253da0ebaSVladimir Oltean 
61353da0ebaSVladimir Oltean 		dsa_slave_setup_tagger(slave);
61453da0ebaSVladimir Oltean 
61553da0ebaSVladimir Oltean 		/* rtnl_mutex is held in dsa_tree_change_tag_proto */
61653da0ebaSVladimir Oltean 		dsa_slave_change_mtu(slave, slave->mtu);
61753da0ebaSVladimir Oltean 	}
61853da0ebaSVladimir Oltean 
61953da0ebaSVladimir Oltean 	return 0;
62053da0ebaSVladimir Oltean }
62153da0ebaSVladimir Oltean 
622c595c433SHoratiu Vultur static int dsa_switch_mrp_add(struct dsa_switch *ds,
623c595c433SHoratiu Vultur 			      struct dsa_notifier_mrp_info *info)
624c595c433SHoratiu Vultur {
625c595c433SHoratiu Vultur 	if (!ds->ops->port_mrp_add)
626c595c433SHoratiu Vultur 		return -EOPNOTSUPP;
627c595c433SHoratiu Vultur 
628f9bcdc36SVladimir Oltean 	if (ds->index == info->sw_index)
629f9bcdc36SVladimir Oltean 		return ds->ops->port_mrp_add(ds, info->port, info->mrp);
630c595c433SHoratiu Vultur 
631f9bcdc36SVladimir Oltean 	return 0;
632c595c433SHoratiu Vultur }
633c595c433SHoratiu Vultur 
634c595c433SHoratiu Vultur static int dsa_switch_mrp_del(struct dsa_switch *ds,
635c595c433SHoratiu Vultur 			      struct dsa_notifier_mrp_info *info)
636c595c433SHoratiu Vultur {
637c595c433SHoratiu Vultur 	if (!ds->ops->port_mrp_del)
638c595c433SHoratiu Vultur 		return -EOPNOTSUPP;
639c595c433SHoratiu Vultur 
640c595c433SHoratiu Vultur 	if (ds->index == info->sw_index)
641c595c433SHoratiu Vultur 		return ds->ops->port_mrp_del(ds, info->port, info->mrp);
642c595c433SHoratiu Vultur 
643c595c433SHoratiu Vultur 	return 0;
644c595c433SHoratiu Vultur }
645c595c433SHoratiu Vultur 
646c595c433SHoratiu Vultur static int
647c595c433SHoratiu Vultur dsa_switch_mrp_add_ring_role(struct dsa_switch *ds,
648c595c433SHoratiu Vultur 			     struct dsa_notifier_mrp_ring_role_info *info)
649c595c433SHoratiu Vultur {
650c595c433SHoratiu Vultur 	if (!ds->ops->port_mrp_add)
651c595c433SHoratiu Vultur 		return -EOPNOTSUPP;
652c595c433SHoratiu Vultur 
653f9bcdc36SVladimir Oltean 	if (ds->index == info->sw_index)
654f9bcdc36SVladimir Oltean 		return ds->ops->port_mrp_add_ring_role(ds, info->port,
655c595c433SHoratiu Vultur 						       info->mrp);
656c595c433SHoratiu Vultur 
657f9bcdc36SVladimir Oltean 	return 0;
658c595c433SHoratiu Vultur }
659c595c433SHoratiu Vultur 
660c595c433SHoratiu Vultur static int
661c595c433SHoratiu Vultur dsa_switch_mrp_del_ring_role(struct dsa_switch *ds,
662c595c433SHoratiu Vultur 			     struct dsa_notifier_mrp_ring_role_info *info)
663c595c433SHoratiu Vultur {
664c595c433SHoratiu Vultur 	if (!ds->ops->port_mrp_del)
665c595c433SHoratiu Vultur 		return -EOPNOTSUPP;
666c595c433SHoratiu Vultur 
667c595c433SHoratiu Vultur 	if (ds->index == info->sw_index)
668c595c433SHoratiu Vultur 		return ds->ops->port_mrp_del_ring_role(ds, info->port,
669c595c433SHoratiu Vultur 						       info->mrp);
670c595c433SHoratiu Vultur 
671c595c433SHoratiu Vultur 	return 0;
672c595c433SHoratiu Vultur }
673c595c433SHoratiu Vultur 
674f515f192SVivien Didelot static int dsa_switch_event(struct notifier_block *nb,
675f515f192SVivien Didelot 			    unsigned long event, void *info)
676f515f192SVivien Didelot {
677f515f192SVivien Didelot 	struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb);
678f515f192SVivien Didelot 	int err;
679f515f192SVivien Didelot 
680f515f192SVivien Didelot 	switch (event) {
6811faabf74SVivien Didelot 	case DSA_NOTIFIER_AGEING_TIME:
6821faabf74SVivien Didelot 		err = dsa_switch_ageing_time(ds, info);
6831faabf74SVivien Didelot 		break;
68404d3a4c6SVivien Didelot 	case DSA_NOTIFIER_BRIDGE_JOIN:
68504d3a4c6SVivien Didelot 		err = dsa_switch_bridge_join(ds, info);
68604d3a4c6SVivien Didelot 		break;
68704d3a4c6SVivien Didelot 	case DSA_NOTIFIER_BRIDGE_LEAVE:
68804d3a4c6SVivien Didelot 		err = dsa_switch_bridge_leave(ds, info);
68904d3a4c6SVivien Didelot 		break;
690685fb6a4SVivien Didelot 	case DSA_NOTIFIER_FDB_ADD:
691685fb6a4SVivien Didelot 		err = dsa_switch_fdb_add(ds, info);
692685fb6a4SVivien Didelot 		break;
693685fb6a4SVivien Didelot 	case DSA_NOTIFIER_FDB_DEL:
694685fb6a4SVivien Didelot 		err = dsa_switch_fdb_del(ds, info);
695685fb6a4SVivien Didelot 		break;
6963dc80afcSVladimir Oltean 	case DSA_NOTIFIER_HOST_FDB_ADD:
6973dc80afcSVladimir Oltean 		err = dsa_switch_host_fdb_add(ds, info);
6983dc80afcSVladimir Oltean 		break;
6993dc80afcSVladimir Oltean 	case DSA_NOTIFIER_HOST_FDB_DEL:
7003dc80afcSVladimir Oltean 		err = dsa_switch_host_fdb_del(ds, info);
7013dc80afcSVladimir Oltean 		break;
70218596f50SGeorge McCollister 	case DSA_NOTIFIER_HSR_JOIN:
70318596f50SGeorge McCollister 		err = dsa_switch_hsr_join(ds, info);
70418596f50SGeorge McCollister 		break;
70518596f50SGeorge McCollister 	case DSA_NOTIFIER_HSR_LEAVE:
70618596f50SGeorge McCollister 		err = dsa_switch_hsr_leave(ds, info);
70718596f50SGeorge McCollister 		break;
708058102a6STobias Waldekranz 	case DSA_NOTIFIER_LAG_CHANGE:
709058102a6STobias Waldekranz 		err = dsa_switch_lag_change(ds, info);
710058102a6STobias Waldekranz 		break;
711058102a6STobias Waldekranz 	case DSA_NOTIFIER_LAG_JOIN:
712058102a6STobias Waldekranz 		err = dsa_switch_lag_join(ds, info);
713058102a6STobias Waldekranz 		break;
714058102a6STobias Waldekranz 	case DSA_NOTIFIER_LAG_LEAVE:
715058102a6STobias Waldekranz 		err = dsa_switch_lag_leave(ds, info);
716058102a6STobias Waldekranz 		break;
7178ae5bcdcSVivien Didelot 	case DSA_NOTIFIER_MDB_ADD:
7188ae5bcdcSVivien Didelot 		err = dsa_switch_mdb_add(ds, info);
7198ae5bcdcSVivien Didelot 		break;
7208ae5bcdcSVivien Didelot 	case DSA_NOTIFIER_MDB_DEL:
7218ae5bcdcSVivien Didelot 		err = dsa_switch_mdb_del(ds, info);
7228ae5bcdcSVivien Didelot 		break;
723b8e997c4SVladimir Oltean 	case DSA_NOTIFIER_HOST_MDB_ADD:
724b8e997c4SVladimir Oltean 		err = dsa_switch_host_mdb_add(ds, info);
725b8e997c4SVladimir Oltean 		break;
726b8e997c4SVladimir Oltean 	case DSA_NOTIFIER_HOST_MDB_DEL:
727b8e997c4SVladimir Oltean 		err = dsa_switch_host_mdb_del(ds, info);
728b8e997c4SVladimir Oltean 		break;
729d0c627b8SVivien Didelot 	case DSA_NOTIFIER_VLAN_ADD:
730d0c627b8SVivien Didelot 		err = dsa_switch_vlan_add(ds, info);
731d0c627b8SVivien Didelot 		break;
732d0c627b8SVivien Didelot 	case DSA_NOTIFIER_VLAN_DEL:
733d0c627b8SVivien Didelot 		err = dsa_switch_vlan_del(ds, info);
734d0c627b8SVivien Didelot 		break;
735bfcb8132SVladimir Oltean 	case DSA_NOTIFIER_MTU:
736bfcb8132SVladimir Oltean 		err = dsa_switch_mtu(ds, info);
737bfcb8132SVladimir Oltean 		break;
73853da0ebaSVladimir Oltean 	case DSA_NOTIFIER_TAG_PROTO:
73953da0ebaSVladimir Oltean 		err = dsa_switch_change_tag_proto(ds, info);
74053da0ebaSVladimir Oltean 		break;
741c595c433SHoratiu Vultur 	case DSA_NOTIFIER_MRP_ADD:
742c595c433SHoratiu Vultur 		err = dsa_switch_mrp_add(ds, info);
743c595c433SHoratiu Vultur 		break;
744c595c433SHoratiu Vultur 	case DSA_NOTIFIER_MRP_DEL:
745c595c433SHoratiu Vultur 		err = dsa_switch_mrp_del(ds, info);
746c595c433SHoratiu Vultur 		break;
747c595c433SHoratiu Vultur 	case DSA_NOTIFIER_MRP_ADD_RING_ROLE:
748c595c433SHoratiu Vultur 		err = dsa_switch_mrp_add_ring_role(ds, info);
749c595c433SHoratiu Vultur 		break;
750c595c433SHoratiu Vultur 	case DSA_NOTIFIER_MRP_DEL_RING_ROLE:
751c595c433SHoratiu Vultur 		err = dsa_switch_mrp_del_ring_role(ds, info);
752c595c433SHoratiu Vultur 		break;
753c64b9c05SVladimir Oltean 	case DSA_NOTIFIER_TAG_8021Q_VLAN_ADD:
754c64b9c05SVladimir Oltean 		err = dsa_switch_tag_8021q_vlan_add(ds, info);
755c64b9c05SVladimir Oltean 		break;
756c64b9c05SVladimir Oltean 	case DSA_NOTIFIER_TAG_8021Q_VLAN_DEL:
757c64b9c05SVladimir Oltean 		err = dsa_switch_tag_8021q_vlan_del(ds, info);
758c64b9c05SVladimir Oltean 		break;
759f515f192SVivien Didelot 	default:
760f515f192SVivien Didelot 		err = -EOPNOTSUPP;
761f515f192SVivien Didelot 		break;
762f515f192SVivien Didelot 	}
763f515f192SVivien Didelot 
764f515f192SVivien Didelot 	if (err)
765f515f192SVivien Didelot 		dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n",
766f515f192SVivien Didelot 			event, err);
767f515f192SVivien Didelot 
768f515f192SVivien Didelot 	return notifier_from_errno(err);
769f515f192SVivien Didelot }
770f515f192SVivien Didelot 
771f515f192SVivien Didelot int dsa_switch_register_notifier(struct dsa_switch *ds)
772f515f192SVivien Didelot {
773f515f192SVivien Didelot 	ds->nb.notifier_call = dsa_switch_event;
774f515f192SVivien Didelot 
775f515f192SVivien Didelot 	return raw_notifier_chain_register(&ds->dst->nh, &ds->nb);
776f515f192SVivien Didelot }
777f515f192SVivien Didelot 
778f515f192SVivien Didelot void dsa_switch_unregister_notifier(struct dsa_switch *ds)
779f515f192SVivien Didelot {
780f515f192SVivien Didelot 	int err;
781f515f192SVivien Didelot 
782f515f192SVivien Didelot 	err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb);
783f515f192SVivien Didelot 	if (err)
784f515f192SVivien Didelot 		dev_err(ds->dev, "failed to unregister notifier (%d)\n", err);
785f515f192SVivien Didelot }
786