xref: /linux/net/dsa/switch.c (revision 479dc497db8322ae486f8ee49020f5600a77a680)
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;
10989153ed6SVladimir Oltean 	struct netlink_ext_ack extack = {0};
110*479dc497SVladimir Oltean 	int err, port;
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
127*479dc497SVladimir Oltean 	 * vlan_filtering callback is only when the last port leaves the last
128*479dc497SVladimir Oltean 	 * VLAN-aware bridge.
129d371b7c9SVladimir Oltean 	 */
130d371b7c9SVladimir Oltean 	if (unset_vlan_filtering && ds->vlan_filtering_is_global) {
131*479dc497SVladimir Oltean 		for (port = 0; port < ds->num_ports; port++) {
132*479dc497SVladimir Oltean 			struct net_device *bridge_dev;
133*479dc497SVladimir Oltean 
134*479dc497SVladimir Oltean 			bridge_dev = dsa_to_port(ds, port)->bridge_dev;
135*479dc497SVladimir Oltean 
136*479dc497SVladimir Oltean 			if (bridge_dev && br_vlan_enabled(bridge_dev)) {
137d371b7c9SVladimir Oltean 				unset_vlan_filtering = false;
138d371b7c9SVladimir Oltean 				break;
139d371b7c9SVladimir Oltean 			}
140d371b7c9SVladimir Oltean 		}
141d371b7c9SVladimir Oltean 	}
142d371b7c9SVladimir Oltean 	if (unset_vlan_filtering) {
1432e554a7aSVladimir Oltean 		err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port),
14489153ed6SVladimir Oltean 					      false, &extack);
14589153ed6SVladimir Oltean 		if (extack._msg)
14689153ed6SVladimir Oltean 			dev_err(ds->dev, "port %d: %s\n", info->port,
14789153ed6SVladimir Oltean 				extack._msg);
148d371b7c9SVladimir Oltean 		if (err && err != EOPNOTSUPP)
149d371b7c9SVladimir Oltean 			return err;
150d371b7c9SVladimir Oltean 	}
15104d3a4c6SVivien Didelot 	return 0;
15204d3a4c6SVivien Didelot }
15304d3a4c6SVivien Didelot 
154685fb6a4SVivien Didelot static int dsa_switch_fdb_add(struct dsa_switch *ds,
155685fb6a4SVivien Didelot 			      struct dsa_notifier_fdb_info *info)
156685fb6a4SVivien Didelot {
1573169241fSVivien Didelot 	int port = dsa_towards_port(ds, info->sw_index, info->port);
158685fb6a4SVivien Didelot 
1591b6dd556SArkadi Sharshevsky 	if (!ds->ops->port_fdb_add)
160685fb6a4SVivien Didelot 		return -EOPNOTSUPP;
161685fb6a4SVivien Didelot 
1623169241fSVivien Didelot 	return ds->ops->port_fdb_add(ds, port, info->addr, info->vid);
163685fb6a4SVivien Didelot }
164685fb6a4SVivien Didelot 
165685fb6a4SVivien Didelot static int dsa_switch_fdb_del(struct dsa_switch *ds,
166685fb6a4SVivien Didelot 			      struct dsa_notifier_fdb_info *info)
167685fb6a4SVivien Didelot {
1683169241fSVivien Didelot 	int port = dsa_towards_port(ds, info->sw_index, info->port);
169685fb6a4SVivien Didelot 
170685fb6a4SVivien Didelot 	if (!ds->ops->port_fdb_del)
171685fb6a4SVivien Didelot 		return -EOPNOTSUPP;
172685fb6a4SVivien Didelot 
1733169241fSVivien Didelot 	return ds->ops->port_fdb_del(ds, port, info->addr, info->vid);
174685fb6a4SVivien Didelot }
175685fb6a4SVivien Didelot 
17618596f50SGeorge McCollister static int dsa_switch_hsr_join(struct dsa_switch *ds,
17718596f50SGeorge McCollister 			       struct dsa_notifier_hsr_info *info)
17818596f50SGeorge McCollister {
17918596f50SGeorge McCollister 	if (ds->index == info->sw_index && ds->ops->port_hsr_join)
18018596f50SGeorge McCollister 		return ds->ops->port_hsr_join(ds, info->port, info->hsr);
18118596f50SGeorge McCollister 
18218596f50SGeorge McCollister 	return -EOPNOTSUPP;
18318596f50SGeorge McCollister }
18418596f50SGeorge McCollister 
18518596f50SGeorge McCollister static int dsa_switch_hsr_leave(struct dsa_switch *ds,
18618596f50SGeorge McCollister 				struct dsa_notifier_hsr_info *info)
18718596f50SGeorge McCollister {
18818596f50SGeorge McCollister 	if (ds->index == info->sw_index && ds->ops->port_hsr_leave)
18918596f50SGeorge McCollister 		return ds->ops->port_hsr_leave(ds, info->port, info->hsr);
19018596f50SGeorge McCollister 
19118596f50SGeorge McCollister 	return -EOPNOTSUPP;
19218596f50SGeorge McCollister }
19318596f50SGeorge McCollister 
194058102a6STobias Waldekranz static int dsa_switch_lag_change(struct dsa_switch *ds,
195058102a6STobias Waldekranz 				 struct dsa_notifier_lag_info *info)
196058102a6STobias Waldekranz {
197058102a6STobias Waldekranz 	if (ds->index == info->sw_index && ds->ops->port_lag_change)
198058102a6STobias Waldekranz 		return ds->ops->port_lag_change(ds, info->port);
199058102a6STobias Waldekranz 
200058102a6STobias Waldekranz 	if (ds->index != info->sw_index && ds->ops->crosschip_lag_change)
201058102a6STobias Waldekranz 		return ds->ops->crosschip_lag_change(ds, info->sw_index,
202058102a6STobias Waldekranz 						     info->port);
203058102a6STobias Waldekranz 
204058102a6STobias Waldekranz 	return 0;
205058102a6STobias Waldekranz }
206058102a6STobias Waldekranz 
207058102a6STobias Waldekranz static int dsa_switch_lag_join(struct dsa_switch *ds,
208058102a6STobias Waldekranz 			       struct dsa_notifier_lag_info *info)
209058102a6STobias Waldekranz {
210058102a6STobias Waldekranz 	if (ds->index == info->sw_index && ds->ops->port_lag_join)
211058102a6STobias Waldekranz 		return ds->ops->port_lag_join(ds, info->port, info->lag,
212058102a6STobias Waldekranz 					      info->info);
213058102a6STobias Waldekranz 
214058102a6STobias Waldekranz 	if (ds->index != info->sw_index && ds->ops->crosschip_lag_join)
215058102a6STobias Waldekranz 		return ds->ops->crosschip_lag_join(ds, info->sw_index,
216058102a6STobias Waldekranz 						   info->port, info->lag,
217058102a6STobias Waldekranz 						   info->info);
218058102a6STobias Waldekranz 
219058102a6STobias Waldekranz 	return 0;
220058102a6STobias Waldekranz }
221058102a6STobias Waldekranz 
222058102a6STobias Waldekranz static int dsa_switch_lag_leave(struct dsa_switch *ds,
223058102a6STobias Waldekranz 				struct dsa_notifier_lag_info *info)
224058102a6STobias Waldekranz {
225058102a6STobias Waldekranz 	if (ds->index == info->sw_index && ds->ops->port_lag_leave)
226058102a6STobias Waldekranz 		return ds->ops->port_lag_leave(ds, info->port, info->lag);
227058102a6STobias Waldekranz 
228058102a6STobias Waldekranz 	if (ds->index != info->sw_index && ds->ops->crosschip_lag_leave)
229058102a6STobias Waldekranz 		return ds->ops->crosschip_lag_leave(ds, info->sw_index,
230058102a6STobias Waldekranz 						    info->port, info->lag);
231058102a6STobias Waldekranz 
232058102a6STobias Waldekranz 	return 0;
233058102a6STobias Waldekranz }
234058102a6STobias Waldekranz 
235e65d45ccSVivien Didelot static bool dsa_switch_mdb_match(struct dsa_switch *ds, int port,
236e65d45ccSVivien Didelot 				 struct dsa_notifier_mdb_info *info)
237e65d45ccSVivien Didelot {
238e65d45ccSVivien Didelot 	if (ds->index == info->sw_index && port == info->port)
239e65d45ccSVivien Didelot 		return true;
240e65d45ccSVivien Didelot 
241e65d45ccSVivien Didelot 	if (dsa_is_dsa_port(ds, port))
242e65d45ccSVivien Didelot 		return true;
243e65d45ccSVivien Didelot 
244e65d45ccSVivien Didelot 	return false;
245e65d45ccSVivien Didelot }
246e65d45ccSVivien Didelot 
247ffb68fc5SVladimir Oltean static int dsa_switch_mdb_add(struct dsa_switch *ds,
248e65d45ccSVivien Didelot 			      struct dsa_notifier_mdb_info *info)
249e6db98dbSVivien Didelot {
250a52b2da7SVladimir Oltean 	int err = 0;
251a52b2da7SVladimir Oltean 	int port;
252e6db98dbSVivien Didelot 
253a52b2da7SVladimir Oltean 	if (!ds->ops->port_mdb_add)
254e6db98dbSVivien Didelot 		return -EOPNOTSUPP;
255e6db98dbSVivien Didelot 
256e65d45ccSVivien Didelot 	for (port = 0; port < ds->num_ports; port++) {
257e65d45ccSVivien Didelot 		if (dsa_switch_mdb_match(ds, port, info)) {
258a52b2da7SVladimir Oltean 			err = ds->ops->port_mdb_add(ds, port, info->mdb);
259e6db98dbSVivien Didelot 			if (err)
260a52b2da7SVladimir Oltean 				break;
261a52b2da7SVladimir Oltean 		}
262a52b2da7SVladimir Oltean 	}
263a52b2da7SVladimir Oltean 
264e6db98dbSVivien Didelot 	return err;
265e6db98dbSVivien Didelot }
2668ae5bcdcSVivien Didelot 
2678ae5bcdcSVivien Didelot static int dsa_switch_mdb_del(struct dsa_switch *ds,
2688ae5bcdcSVivien Didelot 			      struct dsa_notifier_mdb_info *info)
2698ae5bcdcSVivien Didelot {
2708ae5bcdcSVivien Didelot 	if (!ds->ops->port_mdb_del)
2718ae5bcdcSVivien Didelot 		return -EOPNOTSUPP;
2728ae5bcdcSVivien Didelot 
273a1a6b7eaSVivien Didelot 	if (ds->index == info->sw_index)
274e65d45ccSVivien Didelot 		return ds->ops->port_mdb_del(ds, info->port, info->mdb);
275a1a6b7eaSVivien Didelot 
276a1a6b7eaSVivien Didelot 	return 0;
2778ae5bcdcSVivien Didelot }
2788ae5bcdcSVivien Didelot 
279e65d45ccSVivien Didelot static bool dsa_switch_vlan_match(struct dsa_switch *ds, int port,
280e65d45ccSVivien Didelot 				  struct dsa_notifier_vlan_info *info)
281e65d45ccSVivien Didelot {
282e65d45ccSVivien Didelot 	if (ds->index == info->sw_index && port == info->port)
283e65d45ccSVivien Didelot 		return true;
284e65d45ccSVivien Didelot 
2857e1741b4SVivien Didelot 	if (dsa_is_dsa_port(ds, port))
286e65d45ccSVivien Didelot 		return true;
287e65d45ccSVivien Didelot 
288e65d45ccSVivien Didelot 	return false;
289e65d45ccSVivien Didelot }
290e65d45ccSVivien Didelot 
291ffb68fc5SVladimir Oltean static int dsa_switch_vlan_add(struct dsa_switch *ds,
292e65d45ccSVivien Didelot 			       struct dsa_notifier_vlan_info *info)
2939c428c59SVivien Didelot {
2949c428c59SVivien Didelot 	int port, err;
2959c428c59SVivien Didelot 
2961958d581SVladimir Oltean 	if (!ds->ops->port_vlan_add)
2979c428c59SVivien Didelot 		return -EOPNOTSUPP;
2989c428c59SVivien Didelot 
299e65d45ccSVivien Didelot 	for (port = 0; port < ds->num_ports; port++) {
300e65d45ccSVivien Didelot 		if (dsa_switch_vlan_match(ds, port, info)) {
30131046a5fSVladimir Oltean 			err = ds->ops->port_vlan_add(ds, port, info->vlan,
30231046a5fSVladimir Oltean 						     info->extack);
3039c428c59SVivien Didelot 			if (err)
3049c428c59SVivien Didelot 				return err;
3059c428c59SVivien Didelot 		}
306e65d45ccSVivien Didelot 	}
3079c428c59SVivien Didelot 
308d0c627b8SVivien Didelot 	return 0;
309d0c627b8SVivien Didelot }
310d0c627b8SVivien Didelot 
311d0c627b8SVivien Didelot static int dsa_switch_vlan_del(struct dsa_switch *ds,
312d0c627b8SVivien Didelot 			       struct dsa_notifier_vlan_info *info)
313d0c627b8SVivien Didelot {
314d0c627b8SVivien Didelot 	if (!ds->ops->port_vlan_del)
315d0c627b8SVivien Didelot 		return -EOPNOTSUPP;
316d0c627b8SVivien Didelot 
3171ca4aa9cSVivien Didelot 	if (ds->index == info->sw_index)
318e65d45ccSVivien Didelot 		return ds->ops->port_vlan_del(ds, info->port, info->vlan);
3191ca4aa9cSVivien Didelot 
3207e1741b4SVivien Didelot 	/* Do not deprogram the DSA links as they may be used as conduit
3217e1741b4SVivien Didelot 	 * for other VLAN members in the fabric.
3227e1741b4SVivien Didelot 	 */
3231ca4aa9cSVivien Didelot 	return 0;
324d0c627b8SVivien Didelot }
325d0c627b8SVivien Didelot 
32653da0ebaSVladimir Oltean static bool dsa_switch_tag_proto_match(struct dsa_switch *ds, int port,
32753da0ebaSVladimir Oltean 				       struct dsa_notifier_tag_proto_info *info)
32853da0ebaSVladimir Oltean {
32953da0ebaSVladimir Oltean 	if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
33053da0ebaSVladimir Oltean 		return true;
33153da0ebaSVladimir Oltean 
33253da0ebaSVladimir Oltean 	return false;
33353da0ebaSVladimir Oltean }
33453da0ebaSVladimir Oltean 
33553da0ebaSVladimir Oltean static int dsa_switch_change_tag_proto(struct dsa_switch *ds,
33653da0ebaSVladimir Oltean 				       struct dsa_notifier_tag_proto_info *info)
33753da0ebaSVladimir Oltean {
33853da0ebaSVladimir Oltean 	const struct dsa_device_ops *tag_ops = info->tag_ops;
33953da0ebaSVladimir Oltean 	int port, err;
34053da0ebaSVladimir Oltean 
34153da0ebaSVladimir Oltean 	if (!ds->ops->change_tag_protocol)
34253da0ebaSVladimir Oltean 		return -EOPNOTSUPP;
34353da0ebaSVladimir Oltean 
34453da0ebaSVladimir Oltean 	ASSERT_RTNL();
34553da0ebaSVladimir Oltean 
34653da0ebaSVladimir Oltean 	for (port = 0; port < ds->num_ports; port++) {
34753da0ebaSVladimir Oltean 		if (dsa_switch_tag_proto_match(ds, port, info)) {
34853da0ebaSVladimir Oltean 			err = ds->ops->change_tag_protocol(ds, port,
34953da0ebaSVladimir Oltean 							   tag_ops->proto);
35053da0ebaSVladimir Oltean 			if (err)
35153da0ebaSVladimir Oltean 				return err;
35253da0ebaSVladimir Oltean 
35353da0ebaSVladimir Oltean 			if (dsa_is_cpu_port(ds, port))
35453da0ebaSVladimir Oltean 				dsa_port_set_tag_protocol(dsa_to_port(ds, port),
35553da0ebaSVladimir Oltean 							  tag_ops);
35653da0ebaSVladimir Oltean 		}
35753da0ebaSVladimir Oltean 	}
35853da0ebaSVladimir Oltean 
35953da0ebaSVladimir Oltean 	/* Now that changing the tag protocol can no longer fail, let's update
36053da0ebaSVladimir Oltean 	 * the remaining bits which are "duplicated for faster access", and the
36153da0ebaSVladimir Oltean 	 * bits that depend on the tagger, such as the MTU.
36253da0ebaSVladimir Oltean 	 */
36353da0ebaSVladimir Oltean 	for (port = 0; port < ds->num_ports; port++) {
36453da0ebaSVladimir Oltean 		if (dsa_is_user_port(ds, port)) {
36553da0ebaSVladimir Oltean 			struct net_device *slave;
36653da0ebaSVladimir Oltean 
36753da0ebaSVladimir Oltean 			slave = dsa_to_port(ds, port)->slave;
36853da0ebaSVladimir Oltean 			dsa_slave_setup_tagger(slave);
36953da0ebaSVladimir Oltean 
37053da0ebaSVladimir Oltean 			/* rtnl_mutex is held in dsa_tree_change_tag_proto */
37153da0ebaSVladimir Oltean 			dsa_slave_change_mtu(slave, slave->mtu);
37253da0ebaSVladimir Oltean 		}
37353da0ebaSVladimir Oltean 	}
37453da0ebaSVladimir Oltean 
37553da0ebaSVladimir Oltean 	return 0;
37653da0ebaSVladimir Oltean }
37753da0ebaSVladimir Oltean 
378c595c433SHoratiu Vultur static bool dsa_switch_mrp_match(struct dsa_switch *ds, int port,
379c595c433SHoratiu Vultur 				 struct dsa_notifier_mrp_info *info)
380c595c433SHoratiu Vultur {
381c595c433SHoratiu Vultur 	if (ds->index == info->sw_index && port == info->port)
382c595c433SHoratiu Vultur 		return true;
383c595c433SHoratiu Vultur 
384c595c433SHoratiu Vultur 	if (dsa_is_dsa_port(ds, port))
385c595c433SHoratiu Vultur 		return true;
386c595c433SHoratiu Vultur 
387c595c433SHoratiu Vultur 	return false;
388c595c433SHoratiu Vultur }
389c595c433SHoratiu Vultur 
390c595c433SHoratiu Vultur static int dsa_switch_mrp_add(struct dsa_switch *ds,
391c595c433SHoratiu Vultur 			      struct dsa_notifier_mrp_info *info)
392c595c433SHoratiu Vultur {
393c595c433SHoratiu Vultur 	int err = 0;
394c595c433SHoratiu Vultur 	int port;
395c595c433SHoratiu Vultur 
396c595c433SHoratiu Vultur 	if (!ds->ops->port_mrp_add)
397c595c433SHoratiu Vultur 		return -EOPNOTSUPP;
398c595c433SHoratiu Vultur 
399c595c433SHoratiu Vultur 	for (port = 0; port < ds->num_ports; port++) {
400c595c433SHoratiu Vultur 		if (dsa_switch_mrp_match(ds, port, info)) {
401c595c433SHoratiu Vultur 			err = ds->ops->port_mrp_add(ds, port, info->mrp);
402c595c433SHoratiu Vultur 			if (err)
403c595c433SHoratiu Vultur 				break;
404c595c433SHoratiu Vultur 		}
405c595c433SHoratiu Vultur 	}
406c595c433SHoratiu Vultur 
407c595c433SHoratiu Vultur 	return err;
408c595c433SHoratiu Vultur }
409c595c433SHoratiu Vultur 
410c595c433SHoratiu Vultur static int dsa_switch_mrp_del(struct dsa_switch *ds,
411c595c433SHoratiu Vultur 			      struct dsa_notifier_mrp_info *info)
412c595c433SHoratiu Vultur {
413c595c433SHoratiu Vultur 	if (!ds->ops->port_mrp_del)
414c595c433SHoratiu Vultur 		return -EOPNOTSUPP;
415c595c433SHoratiu Vultur 
416c595c433SHoratiu Vultur 	if (ds->index == info->sw_index)
417c595c433SHoratiu Vultur 		return ds->ops->port_mrp_del(ds, info->port, info->mrp);
418c595c433SHoratiu Vultur 
419c595c433SHoratiu Vultur 	return 0;
420c595c433SHoratiu Vultur }
421c595c433SHoratiu Vultur 
422c595c433SHoratiu Vultur static bool
423c595c433SHoratiu Vultur dsa_switch_mrp_ring_role_match(struct dsa_switch *ds, int port,
424c595c433SHoratiu Vultur 			       struct dsa_notifier_mrp_ring_role_info *info)
425c595c433SHoratiu Vultur {
426c595c433SHoratiu Vultur 	if (ds->index == info->sw_index && port == info->port)
427c595c433SHoratiu Vultur 		return true;
428c595c433SHoratiu Vultur 
429c595c433SHoratiu Vultur 	if (dsa_is_dsa_port(ds, port))
430c595c433SHoratiu Vultur 		return true;
431c595c433SHoratiu Vultur 
432c595c433SHoratiu Vultur 	return false;
433c595c433SHoratiu Vultur }
434c595c433SHoratiu Vultur 
435c595c433SHoratiu Vultur static int
436c595c433SHoratiu Vultur dsa_switch_mrp_add_ring_role(struct dsa_switch *ds,
437c595c433SHoratiu Vultur 			     struct dsa_notifier_mrp_ring_role_info *info)
438c595c433SHoratiu Vultur {
439c595c433SHoratiu Vultur 	int err = 0;
440c595c433SHoratiu Vultur 	int port;
441c595c433SHoratiu Vultur 
442c595c433SHoratiu Vultur 	if (!ds->ops->port_mrp_add)
443c595c433SHoratiu Vultur 		return -EOPNOTSUPP;
444c595c433SHoratiu Vultur 
445c595c433SHoratiu Vultur 	for (port = 0; port < ds->num_ports; port++) {
446c595c433SHoratiu Vultur 		if (dsa_switch_mrp_ring_role_match(ds, port, info)) {
447c595c433SHoratiu Vultur 			err = ds->ops->port_mrp_add_ring_role(ds, port,
448c595c433SHoratiu Vultur 							      info->mrp);
449c595c433SHoratiu Vultur 			if (err)
450c595c433SHoratiu Vultur 				break;
451c595c433SHoratiu Vultur 		}
452c595c433SHoratiu Vultur 	}
453c595c433SHoratiu Vultur 
454c595c433SHoratiu Vultur 	return err;
455c595c433SHoratiu Vultur }
456c595c433SHoratiu Vultur 
457c595c433SHoratiu Vultur static int
458c595c433SHoratiu Vultur dsa_switch_mrp_del_ring_role(struct dsa_switch *ds,
459c595c433SHoratiu Vultur 			     struct dsa_notifier_mrp_ring_role_info *info)
460c595c433SHoratiu Vultur {
461c595c433SHoratiu Vultur 	if (!ds->ops->port_mrp_del)
462c595c433SHoratiu Vultur 		return -EOPNOTSUPP;
463c595c433SHoratiu Vultur 
464c595c433SHoratiu Vultur 	if (ds->index == info->sw_index)
465c595c433SHoratiu Vultur 		return ds->ops->port_mrp_del_ring_role(ds, info->port,
466c595c433SHoratiu Vultur 						       info->mrp);
467c595c433SHoratiu Vultur 
468c595c433SHoratiu Vultur 	return 0;
469c595c433SHoratiu Vultur }
470c595c433SHoratiu Vultur 
471f515f192SVivien Didelot static int dsa_switch_event(struct notifier_block *nb,
472f515f192SVivien Didelot 			    unsigned long event, void *info)
473f515f192SVivien Didelot {
474f515f192SVivien Didelot 	struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb);
475f515f192SVivien Didelot 	int err;
476f515f192SVivien Didelot 
477f515f192SVivien Didelot 	switch (event) {
4781faabf74SVivien Didelot 	case DSA_NOTIFIER_AGEING_TIME:
4791faabf74SVivien Didelot 		err = dsa_switch_ageing_time(ds, info);
4801faabf74SVivien Didelot 		break;
48104d3a4c6SVivien Didelot 	case DSA_NOTIFIER_BRIDGE_JOIN:
48204d3a4c6SVivien Didelot 		err = dsa_switch_bridge_join(ds, info);
48304d3a4c6SVivien Didelot 		break;
48404d3a4c6SVivien Didelot 	case DSA_NOTIFIER_BRIDGE_LEAVE:
48504d3a4c6SVivien Didelot 		err = dsa_switch_bridge_leave(ds, info);
48604d3a4c6SVivien Didelot 		break;
487685fb6a4SVivien Didelot 	case DSA_NOTIFIER_FDB_ADD:
488685fb6a4SVivien Didelot 		err = dsa_switch_fdb_add(ds, info);
489685fb6a4SVivien Didelot 		break;
490685fb6a4SVivien Didelot 	case DSA_NOTIFIER_FDB_DEL:
491685fb6a4SVivien Didelot 		err = dsa_switch_fdb_del(ds, info);
492685fb6a4SVivien Didelot 		break;
49318596f50SGeorge McCollister 	case DSA_NOTIFIER_HSR_JOIN:
49418596f50SGeorge McCollister 		err = dsa_switch_hsr_join(ds, info);
49518596f50SGeorge McCollister 		break;
49618596f50SGeorge McCollister 	case DSA_NOTIFIER_HSR_LEAVE:
49718596f50SGeorge McCollister 		err = dsa_switch_hsr_leave(ds, info);
49818596f50SGeorge McCollister 		break;
499058102a6STobias Waldekranz 	case DSA_NOTIFIER_LAG_CHANGE:
500058102a6STobias Waldekranz 		err = dsa_switch_lag_change(ds, info);
501058102a6STobias Waldekranz 		break;
502058102a6STobias Waldekranz 	case DSA_NOTIFIER_LAG_JOIN:
503058102a6STobias Waldekranz 		err = dsa_switch_lag_join(ds, info);
504058102a6STobias Waldekranz 		break;
505058102a6STobias Waldekranz 	case DSA_NOTIFIER_LAG_LEAVE:
506058102a6STobias Waldekranz 		err = dsa_switch_lag_leave(ds, info);
507058102a6STobias Waldekranz 		break;
5088ae5bcdcSVivien Didelot 	case DSA_NOTIFIER_MDB_ADD:
5098ae5bcdcSVivien Didelot 		err = dsa_switch_mdb_add(ds, info);
5108ae5bcdcSVivien Didelot 		break;
5118ae5bcdcSVivien Didelot 	case DSA_NOTIFIER_MDB_DEL:
5128ae5bcdcSVivien Didelot 		err = dsa_switch_mdb_del(ds, info);
5138ae5bcdcSVivien Didelot 		break;
514d0c627b8SVivien Didelot 	case DSA_NOTIFIER_VLAN_ADD:
515d0c627b8SVivien Didelot 		err = dsa_switch_vlan_add(ds, info);
516d0c627b8SVivien Didelot 		break;
517d0c627b8SVivien Didelot 	case DSA_NOTIFIER_VLAN_DEL:
518d0c627b8SVivien Didelot 		err = dsa_switch_vlan_del(ds, info);
519d0c627b8SVivien Didelot 		break;
520bfcb8132SVladimir Oltean 	case DSA_NOTIFIER_MTU:
521bfcb8132SVladimir Oltean 		err = dsa_switch_mtu(ds, info);
522bfcb8132SVladimir Oltean 		break;
52353da0ebaSVladimir Oltean 	case DSA_NOTIFIER_TAG_PROTO:
52453da0ebaSVladimir Oltean 		err = dsa_switch_change_tag_proto(ds, info);
52553da0ebaSVladimir Oltean 		break;
526c595c433SHoratiu Vultur 	case DSA_NOTIFIER_MRP_ADD:
527c595c433SHoratiu Vultur 		err = dsa_switch_mrp_add(ds, info);
528c595c433SHoratiu Vultur 		break;
529c595c433SHoratiu Vultur 	case DSA_NOTIFIER_MRP_DEL:
530c595c433SHoratiu Vultur 		err = dsa_switch_mrp_del(ds, info);
531c595c433SHoratiu Vultur 		break;
532c595c433SHoratiu Vultur 	case DSA_NOTIFIER_MRP_ADD_RING_ROLE:
533c595c433SHoratiu Vultur 		err = dsa_switch_mrp_add_ring_role(ds, info);
534c595c433SHoratiu Vultur 		break;
535c595c433SHoratiu Vultur 	case DSA_NOTIFIER_MRP_DEL_RING_ROLE:
536c595c433SHoratiu Vultur 		err = dsa_switch_mrp_del_ring_role(ds, info);
537c595c433SHoratiu Vultur 		break;
538f515f192SVivien Didelot 	default:
539f515f192SVivien Didelot 		err = -EOPNOTSUPP;
540f515f192SVivien Didelot 		break;
541f515f192SVivien Didelot 	}
542f515f192SVivien Didelot 
543f515f192SVivien Didelot 	if (err)
544f515f192SVivien Didelot 		dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n",
545f515f192SVivien Didelot 			event, err);
546f515f192SVivien Didelot 
547f515f192SVivien Didelot 	return notifier_from_errno(err);
548f515f192SVivien Didelot }
549f515f192SVivien Didelot 
550f515f192SVivien Didelot int dsa_switch_register_notifier(struct dsa_switch *ds)
551f515f192SVivien Didelot {
552f515f192SVivien Didelot 	ds->nb.notifier_call = dsa_switch_event;
553f515f192SVivien Didelot 
554f515f192SVivien Didelot 	return raw_notifier_chain_register(&ds->dst->nh, &ds->nb);
555f515f192SVivien Didelot }
556f515f192SVivien Didelot 
557f515f192SVivien Didelot void dsa_switch_unregister_notifier(struct dsa_switch *ds)
558f515f192SVivien Didelot {
559f515f192SVivien Didelot 	int err;
560f515f192SVivien Didelot 
561f515f192SVivien Didelot 	err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb);
562f515f192SVivien Didelot 	if (err)
563f515f192SVivien Didelot 		dev_err(ds->dev, "failed to unregister notifier (%d)\n", err);
564f515f192SVivien Didelot }
565