xref: /linux/net/dsa/switch.c (revision 89153ed6ebc14879b04686f0e3f3066b1b6bef05)
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 {
201faabf74SVivien Didelot 	int i;
211faabf74SVivien Didelot 
221faabf74SVivien Didelot 	for (i = 0; i < ds->num_ports; ++i) {
2368bb8ea8SVivien Didelot 		struct dsa_port *dp = dsa_to_port(ds, i);
241faabf74SVivien Didelot 
251faabf74SVivien Didelot 		if (dp->ageing_time && dp->ageing_time < ageing_time)
261faabf74SVivien Didelot 			ageing_time = dp->ageing_time;
271faabf74SVivien Didelot 	}
281faabf74SVivien Didelot 
291faabf74SVivien Didelot 	return ageing_time;
301faabf74SVivien Didelot }
311faabf74SVivien Didelot 
321faabf74SVivien Didelot static int dsa_switch_ageing_time(struct dsa_switch *ds,
331faabf74SVivien Didelot 				  struct dsa_notifier_ageing_time_info *info)
341faabf74SVivien Didelot {
351faabf74SVivien Didelot 	unsigned int ageing_time = info->ageing_time;
361faabf74SVivien Didelot 
371faabf74SVivien Didelot 	if (ds->ageing_time_min && ageing_time < ds->ageing_time_min)
381faabf74SVivien Didelot 		return -ERANGE;
3977b61365SVladimir Oltean 
401faabf74SVivien Didelot 	if (ds->ageing_time_max && ageing_time > ds->ageing_time_max)
411faabf74SVivien Didelot 		return -ERANGE;
421faabf74SVivien Didelot 
431faabf74SVivien Didelot 	/* Program the fastest ageing time in case of multiple bridges */
441faabf74SVivien Didelot 	ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time);
451faabf74SVivien Didelot 
461faabf74SVivien Didelot 	if (ds->ops->set_ageing_time)
471faabf74SVivien Didelot 		return ds->ops->set_ageing_time(ds, ageing_time);
481faabf74SVivien Didelot 
491faabf74SVivien Didelot 	return 0;
501faabf74SVivien Didelot }
511faabf74SVivien Didelot 
52bfcb8132SVladimir Oltean static bool dsa_switch_mtu_match(struct dsa_switch *ds, int port,
53bfcb8132SVladimir Oltean 				 struct dsa_notifier_mtu_info *info)
54bfcb8132SVladimir Oltean {
55bfcb8132SVladimir Oltean 	if (ds->index == info->sw_index)
56bfcb8132SVladimir Oltean 		return (port == info->port) || dsa_is_dsa_port(ds, port);
57bfcb8132SVladimir Oltean 
58bfcb8132SVladimir Oltean 	if (!info->propagate_upstream)
59bfcb8132SVladimir Oltean 		return false;
60bfcb8132SVladimir Oltean 
61bfcb8132SVladimir Oltean 	if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port))
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 {
70bfcb8132SVladimir Oltean 	int port, ret;
71bfcb8132SVladimir Oltean 
72bfcb8132SVladimir Oltean 	if (!ds->ops->port_change_mtu)
73bfcb8132SVladimir Oltean 		return -EOPNOTSUPP;
74bfcb8132SVladimir Oltean 
75bfcb8132SVladimir Oltean 	for (port = 0; port < ds->num_ports; port++) {
76bfcb8132SVladimir Oltean 		if (dsa_switch_mtu_match(ds, port, info)) {
77bfcb8132SVladimir Oltean 			ret = ds->ops->port_change_mtu(ds, port, info->mtu);
78bfcb8132SVladimir Oltean 			if (ret)
79bfcb8132SVladimir Oltean 				return ret;
80bfcb8132SVladimir Oltean 		}
81bfcb8132SVladimir Oltean 	}
82bfcb8132SVladimir Oltean 
83bfcb8132SVladimir Oltean 	return 0;
84bfcb8132SVladimir Oltean }
85bfcb8132SVladimir Oltean 
8604d3a4c6SVivien Didelot static int dsa_switch_bridge_join(struct dsa_switch *ds,
8704d3a4c6SVivien Didelot 				  struct dsa_notifier_bridge_info *info)
8804d3a4c6SVivien Didelot {
89f66a6a69SVladimir Oltean 	struct dsa_switch_tree *dst = ds->dst;
90f66a6a69SVladimir Oltean 
91f66a6a69SVladimir Oltean 	if (dst->index == info->tree_index && ds->index == info->sw_index &&
92f66a6a69SVladimir Oltean 	    ds->ops->port_bridge_join)
9304d3a4c6SVivien Didelot 		return ds->ops->port_bridge_join(ds, info->port, info->br);
9404d3a4c6SVivien Didelot 
95f66a6a69SVladimir Oltean 	if ((dst->index != info->tree_index || ds->index != info->sw_index) &&
96f66a6a69SVladimir Oltean 	    ds->ops->crosschip_bridge_join)
97f66a6a69SVladimir Oltean 		return ds->ops->crosschip_bridge_join(ds, info->tree_index,
98f66a6a69SVladimir Oltean 						      info->sw_index,
9940ef2c93SVivien Didelot 						      info->port, info->br);
10004d3a4c6SVivien Didelot 
10104d3a4c6SVivien Didelot 	return 0;
10204d3a4c6SVivien Didelot }
10304d3a4c6SVivien Didelot 
10404d3a4c6SVivien Didelot static int dsa_switch_bridge_leave(struct dsa_switch *ds,
10504d3a4c6SVivien Didelot 				   struct dsa_notifier_bridge_info *info)
10604d3a4c6SVivien Didelot {
107d371b7c9SVladimir Oltean 	bool unset_vlan_filtering = br_vlan_enabled(info->br);
108f66a6a69SVladimir Oltean 	struct dsa_switch_tree *dst = ds->dst;
109*89153ed6SVladimir Oltean 	struct netlink_ext_ack extack = {0};
110d371b7c9SVladimir Oltean 	int err, i;
111d371b7c9SVladimir Oltean 
112f66a6a69SVladimir Oltean 	if (dst->index == info->tree_index && ds->index == info->sw_index &&
113f66a6a69SVladimir Oltean 	    ds->ops->port_bridge_join)
11404d3a4c6SVivien Didelot 		ds->ops->port_bridge_leave(ds, info->port, info->br);
11504d3a4c6SVivien Didelot 
116f66a6a69SVladimir Oltean 	if ((dst->index != info->tree_index || ds->index != info->sw_index) &&
117f66a6a69SVladimir Oltean 	    ds->ops->crosschip_bridge_join)
118f66a6a69SVladimir Oltean 		ds->ops->crosschip_bridge_leave(ds, info->tree_index,
119f66a6a69SVladimir Oltean 						info->sw_index, info->port,
12040ef2c93SVivien Didelot 						info->br);
12104d3a4c6SVivien Didelot 
122d371b7c9SVladimir Oltean 	/* If the bridge was vlan_filtering, the bridge core doesn't trigger an
123d371b7c9SVladimir Oltean 	 * event for changing vlan_filtering setting upon slave ports leaving
124d371b7c9SVladimir Oltean 	 * it. That is a good thing, because that lets us handle it and also
125d371b7c9SVladimir Oltean 	 * handle the case where the switch's vlan_filtering setting is global
126d371b7c9SVladimir Oltean 	 * (not per port). When that happens, the correct moment to trigger the
127d371b7c9SVladimir Oltean 	 * vlan_filtering callback is only when the last port left this bridge.
128d371b7c9SVladimir Oltean 	 */
129d371b7c9SVladimir Oltean 	if (unset_vlan_filtering && ds->vlan_filtering_is_global) {
130d371b7c9SVladimir Oltean 		for (i = 0; i < ds->num_ports; i++) {
131d371b7c9SVladimir Oltean 			if (i == info->port)
132d371b7c9SVladimir Oltean 				continue;
133d371b7c9SVladimir Oltean 			if (dsa_to_port(ds, i)->bridge_dev == info->br) {
134d371b7c9SVladimir Oltean 				unset_vlan_filtering = false;
135d371b7c9SVladimir Oltean 				break;
136d371b7c9SVladimir Oltean 			}
137d371b7c9SVladimir Oltean 		}
138d371b7c9SVladimir Oltean 	}
139d371b7c9SVladimir Oltean 	if (unset_vlan_filtering) {
1402e554a7aSVladimir Oltean 		err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port),
141*89153ed6SVladimir Oltean 					      false, &extack);
142*89153ed6SVladimir Oltean 		if (extack._msg)
143*89153ed6SVladimir Oltean 			dev_err(ds->dev, "port %d: %s\n", info->port,
144*89153ed6SVladimir Oltean 				extack._msg);
145d371b7c9SVladimir Oltean 		if (err && err != EOPNOTSUPP)
146d371b7c9SVladimir Oltean 			return err;
147d371b7c9SVladimir Oltean 	}
14804d3a4c6SVivien Didelot 	return 0;
14904d3a4c6SVivien Didelot }
15004d3a4c6SVivien Didelot 
151685fb6a4SVivien Didelot static int dsa_switch_fdb_add(struct dsa_switch *ds,
152685fb6a4SVivien Didelot 			      struct dsa_notifier_fdb_info *info)
153685fb6a4SVivien Didelot {
1543169241fSVivien Didelot 	int port = dsa_towards_port(ds, info->sw_index, info->port);
155685fb6a4SVivien Didelot 
1561b6dd556SArkadi Sharshevsky 	if (!ds->ops->port_fdb_add)
157685fb6a4SVivien Didelot 		return -EOPNOTSUPP;
158685fb6a4SVivien Didelot 
1593169241fSVivien Didelot 	return ds->ops->port_fdb_add(ds, port, info->addr, info->vid);
160685fb6a4SVivien Didelot }
161685fb6a4SVivien Didelot 
162685fb6a4SVivien Didelot static int dsa_switch_fdb_del(struct dsa_switch *ds,
163685fb6a4SVivien Didelot 			      struct dsa_notifier_fdb_info *info)
164685fb6a4SVivien Didelot {
1653169241fSVivien Didelot 	int port = dsa_towards_port(ds, info->sw_index, info->port);
166685fb6a4SVivien Didelot 
167685fb6a4SVivien Didelot 	if (!ds->ops->port_fdb_del)
168685fb6a4SVivien Didelot 		return -EOPNOTSUPP;
169685fb6a4SVivien Didelot 
1703169241fSVivien Didelot 	return ds->ops->port_fdb_del(ds, port, info->addr, info->vid);
171685fb6a4SVivien Didelot }
172685fb6a4SVivien Didelot 
17318596f50SGeorge McCollister static int dsa_switch_hsr_join(struct dsa_switch *ds,
17418596f50SGeorge McCollister 			       struct dsa_notifier_hsr_info *info)
17518596f50SGeorge McCollister {
17618596f50SGeorge McCollister 	if (ds->index == info->sw_index && ds->ops->port_hsr_join)
17718596f50SGeorge McCollister 		return ds->ops->port_hsr_join(ds, info->port, info->hsr);
17818596f50SGeorge McCollister 
17918596f50SGeorge McCollister 	return -EOPNOTSUPP;
18018596f50SGeorge McCollister }
18118596f50SGeorge McCollister 
18218596f50SGeorge McCollister static int dsa_switch_hsr_leave(struct dsa_switch *ds,
18318596f50SGeorge McCollister 				struct dsa_notifier_hsr_info *info)
18418596f50SGeorge McCollister {
18518596f50SGeorge McCollister 	if (ds->index == info->sw_index && ds->ops->port_hsr_leave)
18618596f50SGeorge McCollister 		return ds->ops->port_hsr_leave(ds, info->port, info->hsr);
18718596f50SGeorge McCollister 
18818596f50SGeorge McCollister 	return -EOPNOTSUPP;
18918596f50SGeorge McCollister }
19018596f50SGeorge McCollister 
191058102a6STobias Waldekranz static int dsa_switch_lag_change(struct dsa_switch *ds,
192058102a6STobias Waldekranz 				 struct dsa_notifier_lag_info *info)
193058102a6STobias Waldekranz {
194058102a6STobias Waldekranz 	if (ds->index == info->sw_index && ds->ops->port_lag_change)
195058102a6STobias Waldekranz 		return ds->ops->port_lag_change(ds, info->port);
196058102a6STobias Waldekranz 
197058102a6STobias Waldekranz 	if (ds->index != info->sw_index && ds->ops->crosschip_lag_change)
198058102a6STobias Waldekranz 		return ds->ops->crosschip_lag_change(ds, info->sw_index,
199058102a6STobias Waldekranz 						     info->port);
200058102a6STobias Waldekranz 
201058102a6STobias Waldekranz 	return 0;
202058102a6STobias Waldekranz }
203058102a6STobias Waldekranz 
204058102a6STobias Waldekranz static int dsa_switch_lag_join(struct dsa_switch *ds,
205058102a6STobias Waldekranz 			       struct dsa_notifier_lag_info *info)
206058102a6STobias Waldekranz {
207058102a6STobias Waldekranz 	if (ds->index == info->sw_index && ds->ops->port_lag_join)
208058102a6STobias Waldekranz 		return ds->ops->port_lag_join(ds, info->port, info->lag,
209058102a6STobias Waldekranz 					      info->info);
210058102a6STobias Waldekranz 
211058102a6STobias Waldekranz 	if (ds->index != info->sw_index && ds->ops->crosschip_lag_join)
212058102a6STobias Waldekranz 		return ds->ops->crosschip_lag_join(ds, info->sw_index,
213058102a6STobias Waldekranz 						   info->port, info->lag,
214058102a6STobias Waldekranz 						   info->info);
215058102a6STobias Waldekranz 
216058102a6STobias Waldekranz 	return 0;
217058102a6STobias Waldekranz }
218058102a6STobias Waldekranz 
219058102a6STobias Waldekranz static int dsa_switch_lag_leave(struct dsa_switch *ds,
220058102a6STobias Waldekranz 				struct dsa_notifier_lag_info *info)
221058102a6STobias Waldekranz {
222058102a6STobias Waldekranz 	if (ds->index == info->sw_index && ds->ops->port_lag_leave)
223058102a6STobias Waldekranz 		return ds->ops->port_lag_leave(ds, info->port, info->lag);
224058102a6STobias Waldekranz 
225058102a6STobias Waldekranz 	if (ds->index != info->sw_index && ds->ops->crosschip_lag_leave)
226058102a6STobias Waldekranz 		return ds->ops->crosschip_lag_leave(ds, info->sw_index,
227058102a6STobias Waldekranz 						    info->port, info->lag);
228058102a6STobias Waldekranz 
229058102a6STobias Waldekranz 	return 0;
230058102a6STobias Waldekranz }
231058102a6STobias Waldekranz 
232e65d45ccSVivien Didelot static bool dsa_switch_mdb_match(struct dsa_switch *ds, int port,
233e65d45ccSVivien Didelot 				 struct dsa_notifier_mdb_info *info)
234e65d45ccSVivien Didelot {
235e65d45ccSVivien Didelot 	if (ds->index == info->sw_index && port == info->port)
236e65d45ccSVivien Didelot 		return true;
237e65d45ccSVivien Didelot 
238e65d45ccSVivien Didelot 	if (dsa_is_dsa_port(ds, port))
239e65d45ccSVivien Didelot 		return true;
240e65d45ccSVivien Didelot 
241e65d45ccSVivien Didelot 	return false;
242e65d45ccSVivien Didelot }
243e65d45ccSVivien Didelot 
244ffb68fc5SVladimir Oltean static int dsa_switch_mdb_add(struct dsa_switch *ds,
245e65d45ccSVivien Didelot 			      struct dsa_notifier_mdb_info *info)
246e6db98dbSVivien Didelot {
247a52b2da7SVladimir Oltean 	int err = 0;
248a52b2da7SVladimir Oltean 	int port;
249e6db98dbSVivien Didelot 
250a52b2da7SVladimir Oltean 	if (!ds->ops->port_mdb_add)
251e6db98dbSVivien Didelot 		return -EOPNOTSUPP;
252e6db98dbSVivien Didelot 
253e65d45ccSVivien Didelot 	for (port = 0; port < ds->num_ports; port++) {
254e65d45ccSVivien Didelot 		if (dsa_switch_mdb_match(ds, port, info)) {
255a52b2da7SVladimir Oltean 			err = ds->ops->port_mdb_add(ds, port, info->mdb);
256e6db98dbSVivien Didelot 			if (err)
257a52b2da7SVladimir Oltean 				break;
258a52b2da7SVladimir Oltean 		}
259a52b2da7SVladimir Oltean 	}
260a52b2da7SVladimir Oltean 
261e6db98dbSVivien Didelot 	return err;
262e6db98dbSVivien Didelot }
2638ae5bcdcSVivien Didelot 
2648ae5bcdcSVivien Didelot static int dsa_switch_mdb_del(struct dsa_switch *ds,
2658ae5bcdcSVivien Didelot 			      struct dsa_notifier_mdb_info *info)
2668ae5bcdcSVivien Didelot {
2678ae5bcdcSVivien Didelot 	if (!ds->ops->port_mdb_del)
2688ae5bcdcSVivien Didelot 		return -EOPNOTSUPP;
2698ae5bcdcSVivien Didelot 
270a1a6b7eaSVivien Didelot 	if (ds->index == info->sw_index)
271e65d45ccSVivien Didelot 		return ds->ops->port_mdb_del(ds, info->port, info->mdb);
272a1a6b7eaSVivien Didelot 
273a1a6b7eaSVivien Didelot 	return 0;
2748ae5bcdcSVivien Didelot }
2758ae5bcdcSVivien Didelot 
276e65d45ccSVivien Didelot static bool dsa_switch_vlan_match(struct dsa_switch *ds, int port,
277e65d45ccSVivien Didelot 				  struct dsa_notifier_vlan_info *info)
278e65d45ccSVivien Didelot {
279e65d45ccSVivien Didelot 	if (ds->index == info->sw_index && port == info->port)
280e65d45ccSVivien Didelot 		return true;
281e65d45ccSVivien Didelot 
2827e1741b4SVivien Didelot 	if (dsa_is_dsa_port(ds, port))
283e65d45ccSVivien Didelot 		return true;
284e65d45ccSVivien Didelot 
285e65d45ccSVivien Didelot 	return false;
286e65d45ccSVivien Didelot }
287e65d45ccSVivien Didelot 
288ffb68fc5SVladimir Oltean static int dsa_switch_vlan_add(struct dsa_switch *ds,
289e65d45ccSVivien Didelot 			       struct dsa_notifier_vlan_info *info)
2909c428c59SVivien Didelot {
2919c428c59SVivien Didelot 	int port, err;
2929c428c59SVivien Didelot 
2931958d581SVladimir Oltean 	if (!ds->ops->port_vlan_add)
2949c428c59SVivien Didelot 		return -EOPNOTSUPP;
2959c428c59SVivien Didelot 
296e65d45ccSVivien Didelot 	for (port = 0; port < ds->num_ports; port++) {
297e65d45ccSVivien Didelot 		if (dsa_switch_vlan_match(ds, port, info)) {
29831046a5fSVladimir Oltean 			err = ds->ops->port_vlan_add(ds, port, info->vlan,
29931046a5fSVladimir Oltean 						     info->extack);
3009c428c59SVivien Didelot 			if (err)
3019c428c59SVivien Didelot 				return err;
3029c428c59SVivien Didelot 		}
303e65d45ccSVivien Didelot 	}
3049c428c59SVivien Didelot 
305d0c627b8SVivien Didelot 	return 0;
306d0c627b8SVivien Didelot }
307d0c627b8SVivien Didelot 
308d0c627b8SVivien Didelot static int dsa_switch_vlan_del(struct dsa_switch *ds,
309d0c627b8SVivien Didelot 			       struct dsa_notifier_vlan_info *info)
310d0c627b8SVivien Didelot {
311d0c627b8SVivien Didelot 	if (!ds->ops->port_vlan_del)
312d0c627b8SVivien Didelot 		return -EOPNOTSUPP;
313d0c627b8SVivien Didelot 
3141ca4aa9cSVivien Didelot 	if (ds->index == info->sw_index)
315e65d45ccSVivien Didelot 		return ds->ops->port_vlan_del(ds, info->port, info->vlan);
3161ca4aa9cSVivien Didelot 
3177e1741b4SVivien Didelot 	/* Do not deprogram the DSA links as they may be used as conduit
3187e1741b4SVivien Didelot 	 * for other VLAN members in the fabric.
3197e1741b4SVivien Didelot 	 */
3201ca4aa9cSVivien Didelot 	return 0;
321d0c627b8SVivien Didelot }
322d0c627b8SVivien Didelot 
32353da0ebaSVladimir Oltean static bool dsa_switch_tag_proto_match(struct dsa_switch *ds, int port,
32453da0ebaSVladimir Oltean 				       struct dsa_notifier_tag_proto_info *info)
32553da0ebaSVladimir Oltean {
32653da0ebaSVladimir Oltean 	if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
32753da0ebaSVladimir Oltean 		return true;
32853da0ebaSVladimir Oltean 
32953da0ebaSVladimir Oltean 	return false;
33053da0ebaSVladimir Oltean }
33153da0ebaSVladimir Oltean 
33253da0ebaSVladimir Oltean static int dsa_switch_change_tag_proto(struct dsa_switch *ds,
33353da0ebaSVladimir Oltean 				       struct dsa_notifier_tag_proto_info *info)
33453da0ebaSVladimir Oltean {
33553da0ebaSVladimir Oltean 	const struct dsa_device_ops *tag_ops = info->tag_ops;
33653da0ebaSVladimir Oltean 	int port, err;
33753da0ebaSVladimir Oltean 
33853da0ebaSVladimir Oltean 	if (!ds->ops->change_tag_protocol)
33953da0ebaSVladimir Oltean 		return -EOPNOTSUPP;
34053da0ebaSVladimir Oltean 
34153da0ebaSVladimir Oltean 	ASSERT_RTNL();
34253da0ebaSVladimir Oltean 
34353da0ebaSVladimir Oltean 	for (port = 0; port < ds->num_ports; port++) {
34453da0ebaSVladimir Oltean 		if (dsa_switch_tag_proto_match(ds, port, info)) {
34553da0ebaSVladimir Oltean 			err = ds->ops->change_tag_protocol(ds, port,
34653da0ebaSVladimir Oltean 							   tag_ops->proto);
34753da0ebaSVladimir Oltean 			if (err)
34853da0ebaSVladimir Oltean 				return err;
34953da0ebaSVladimir Oltean 
35053da0ebaSVladimir Oltean 			if (dsa_is_cpu_port(ds, port))
35153da0ebaSVladimir Oltean 				dsa_port_set_tag_protocol(dsa_to_port(ds, port),
35253da0ebaSVladimir Oltean 							  tag_ops);
35353da0ebaSVladimir Oltean 		}
35453da0ebaSVladimir Oltean 	}
35553da0ebaSVladimir Oltean 
35653da0ebaSVladimir Oltean 	/* Now that changing the tag protocol can no longer fail, let's update
35753da0ebaSVladimir Oltean 	 * the remaining bits which are "duplicated for faster access", and the
35853da0ebaSVladimir Oltean 	 * bits that depend on the tagger, such as the MTU.
35953da0ebaSVladimir Oltean 	 */
36053da0ebaSVladimir Oltean 	for (port = 0; port < ds->num_ports; port++) {
36153da0ebaSVladimir Oltean 		if (dsa_is_user_port(ds, port)) {
36253da0ebaSVladimir Oltean 			struct net_device *slave;
36353da0ebaSVladimir Oltean 
36453da0ebaSVladimir Oltean 			slave = dsa_to_port(ds, port)->slave;
36553da0ebaSVladimir Oltean 			dsa_slave_setup_tagger(slave);
36653da0ebaSVladimir Oltean 
36753da0ebaSVladimir Oltean 			/* rtnl_mutex is held in dsa_tree_change_tag_proto */
36853da0ebaSVladimir Oltean 			dsa_slave_change_mtu(slave, slave->mtu);
36953da0ebaSVladimir Oltean 		}
37053da0ebaSVladimir Oltean 	}
37153da0ebaSVladimir Oltean 
37253da0ebaSVladimir Oltean 	return 0;
37353da0ebaSVladimir Oltean }
37453da0ebaSVladimir Oltean 
375f515f192SVivien Didelot static int dsa_switch_event(struct notifier_block *nb,
376f515f192SVivien Didelot 			    unsigned long event, void *info)
377f515f192SVivien Didelot {
378f515f192SVivien Didelot 	struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb);
379f515f192SVivien Didelot 	int err;
380f515f192SVivien Didelot 
381f515f192SVivien Didelot 	switch (event) {
3821faabf74SVivien Didelot 	case DSA_NOTIFIER_AGEING_TIME:
3831faabf74SVivien Didelot 		err = dsa_switch_ageing_time(ds, info);
3841faabf74SVivien Didelot 		break;
38504d3a4c6SVivien Didelot 	case DSA_NOTIFIER_BRIDGE_JOIN:
38604d3a4c6SVivien Didelot 		err = dsa_switch_bridge_join(ds, info);
38704d3a4c6SVivien Didelot 		break;
38804d3a4c6SVivien Didelot 	case DSA_NOTIFIER_BRIDGE_LEAVE:
38904d3a4c6SVivien Didelot 		err = dsa_switch_bridge_leave(ds, info);
39004d3a4c6SVivien Didelot 		break;
391685fb6a4SVivien Didelot 	case DSA_NOTIFIER_FDB_ADD:
392685fb6a4SVivien Didelot 		err = dsa_switch_fdb_add(ds, info);
393685fb6a4SVivien Didelot 		break;
394685fb6a4SVivien Didelot 	case DSA_NOTIFIER_FDB_DEL:
395685fb6a4SVivien Didelot 		err = dsa_switch_fdb_del(ds, info);
396685fb6a4SVivien Didelot 		break;
39718596f50SGeorge McCollister 	case DSA_NOTIFIER_HSR_JOIN:
39818596f50SGeorge McCollister 		err = dsa_switch_hsr_join(ds, info);
39918596f50SGeorge McCollister 		break;
40018596f50SGeorge McCollister 	case DSA_NOTIFIER_HSR_LEAVE:
40118596f50SGeorge McCollister 		err = dsa_switch_hsr_leave(ds, info);
40218596f50SGeorge McCollister 		break;
403058102a6STobias Waldekranz 	case DSA_NOTIFIER_LAG_CHANGE:
404058102a6STobias Waldekranz 		err = dsa_switch_lag_change(ds, info);
405058102a6STobias Waldekranz 		break;
406058102a6STobias Waldekranz 	case DSA_NOTIFIER_LAG_JOIN:
407058102a6STobias Waldekranz 		err = dsa_switch_lag_join(ds, info);
408058102a6STobias Waldekranz 		break;
409058102a6STobias Waldekranz 	case DSA_NOTIFIER_LAG_LEAVE:
410058102a6STobias Waldekranz 		err = dsa_switch_lag_leave(ds, info);
411058102a6STobias Waldekranz 		break;
4128ae5bcdcSVivien Didelot 	case DSA_NOTIFIER_MDB_ADD:
4138ae5bcdcSVivien Didelot 		err = dsa_switch_mdb_add(ds, info);
4148ae5bcdcSVivien Didelot 		break;
4158ae5bcdcSVivien Didelot 	case DSA_NOTIFIER_MDB_DEL:
4168ae5bcdcSVivien Didelot 		err = dsa_switch_mdb_del(ds, info);
4178ae5bcdcSVivien Didelot 		break;
418d0c627b8SVivien Didelot 	case DSA_NOTIFIER_VLAN_ADD:
419d0c627b8SVivien Didelot 		err = dsa_switch_vlan_add(ds, info);
420d0c627b8SVivien Didelot 		break;
421d0c627b8SVivien Didelot 	case DSA_NOTIFIER_VLAN_DEL:
422d0c627b8SVivien Didelot 		err = dsa_switch_vlan_del(ds, info);
423d0c627b8SVivien Didelot 		break;
424bfcb8132SVladimir Oltean 	case DSA_NOTIFIER_MTU:
425bfcb8132SVladimir Oltean 		err = dsa_switch_mtu(ds, info);
426bfcb8132SVladimir Oltean 		break;
42753da0ebaSVladimir Oltean 	case DSA_NOTIFIER_TAG_PROTO:
42853da0ebaSVladimir Oltean 		err = dsa_switch_change_tag_proto(ds, info);
42953da0ebaSVladimir Oltean 		break;
430f515f192SVivien Didelot 	default:
431f515f192SVivien Didelot 		err = -EOPNOTSUPP;
432f515f192SVivien Didelot 		break;
433f515f192SVivien Didelot 	}
434f515f192SVivien Didelot 
435f515f192SVivien Didelot 	if (err)
436f515f192SVivien Didelot 		dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n",
437f515f192SVivien Didelot 			event, err);
438f515f192SVivien Didelot 
439f515f192SVivien Didelot 	return notifier_from_errno(err);
440f515f192SVivien Didelot }
441f515f192SVivien Didelot 
442f515f192SVivien Didelot int dsa_switch_register_notifier(struct dsa_switch *ds)
443f515f192SVivien Didelot {
444f515f192SVivien Didelot 	ds->nb.notifier_call = dsa_switch_event;
445f515f192SVivien Didelot 
446f515f192SVivien Didelot 	return raw_notifier_chain_register(&ds->dst->nh, &ds->nb);
447f515f192SVivien Didelot }
448f515f192SVivien Didelot 
449f515f192SVivien Didelot void dsa_switch_unregister_notifier(struct dsa_switch *ds)
450f515f192SVivien Didelot {
451f515f192SVivien Didelot 	int err;
452f515f192SVivien Didelot 
453f515f192SVivien Didelot 	err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb);
454f515f192SVivien Didelot 	if (err)
455f515f192SVivien Didelot 		dev_err(ds->dev, "failed to unregister notifier (%d)\n", err);
456f515f192SVivien Didelot }
457