xref: /OK3568_Linux_fs/kernel/net/dsa/switch.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Handling of a single switch chip, part of a switch fabric
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (c) 2017 Savoir-faire Linux Inc.
6*4882a593Smuzhiyun  *	Vivien Didelot <vivien.didelot@savoirfairelinux.com>
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #include <linux/if_bridge.h>
10*4882a593Smuzhiyun #include <linux/netdevice.h>
11*4882a593Smuzhiyun #include <linux/notifier.h>
12*4882a593Smuzhiyun #include <linux/if_vlan.h>
13*4882a593Smuzhiyun #include <net/switchdev.h>
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #include "dsa_priv.h"
16*4882a593Smuzhiyun 
dsa_switch_fastest_ageing_time(struct dsa_switch * ds,unsigned int ageing_time)17*4882a593Smuzhiyun static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds,
18*4882a593Smuzhiyun 						   unsigned int ageing_time)
19*4882a593Smuzhiyun {
20*4882a593Smuzhiyun 	int i;
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun 	for (i = 0; i < ds->num_ports; ++i) {
23*4882a593Smuzhiyun 		struct dsa_port *dp = dsa_to_port(ds, i);
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun 		if (dp->ageing_time && dp->ageing_time < ageing_time)
26*4882a593Smuzhiyun 			ageing_time = dp->ageing_time;
27*4882a593Smuzhiyun 	}
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun 	return ageing_time;
30*4882a593Smuzhiyun }
31*4882a593Smuzhiyun 
dsa_switch_ageing_time(struct dsa_switch * ds,struct dsa_notifier_ageing_time_info * info)32*4882a593Smuzhiyun static int dsa_switch_ageing_time(struct dsa_switch *ds,
33*4882a593Smuzhiyun 				  struct dsa_notifier_ageing_time_info *info)
34*4882a593Smuzhiyun {
35*4882a593Smuzhiyun 	unsigned int ageing_time = info->ageing_time;
36*4882a593Smuzhiyun 	struct switchdev_trans *trans = info->trans;
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun 	if (switchdev_trans_ph_prepare(trans)) {
39*4882a593Smuzhiyun 		if (ds->ageing_time_min && ageing_time < ds->ageing_time_min)
40*4882a593Smuzhiyun 			return -ERANGE;
41*4882a593Smuzhiyun 		if (ds->ageing_time_max && ageing_time > ds->ageing_time_max)
42*4882a593Smuzhiyun 			return -ERANGE;
43*4882a593Smuzhiyun 		return 0;
44*4882a593Smuzhiyun 	}
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun 	/* Program the fastest ageing time in case of multiple bridges */
47*4882a593Smuzhiyun 	ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time);
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	if (ds->ops->set_ageing_time)
50*4882a593Smuzhiyun 		return ds->ops->set_ageing_time(ds, ageing_time);
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	return 0;
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun 
dsa_switch_mtu_match(struct dsa_switch * ds,int port,struct dsa_notifier_mtu_info * info)55*4882a593Smuzhiyun static bool dsa_switch_mtu_match(struct dsa_switch *ds, int port,
56*4882a593Smuzhiyun 				 struct dsa_notifier_mtu_info *info)
57*4882a593Smuzhiyun {
58*4882a593Smuzhiyun 	if (ds->index == info->sw_index)
59*4882a593Smuzhiyun 		return (port == info->port) || dsa_is_dsa_port(ds, port);
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	if (!info->propagate_upstream)
62*4882a593Smuzhiyun 		return false;
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port))
65*4882a593Smuzhiyun 		return true;
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	return false;
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun 
dsa_switch_mtu(struct dsa_switch * ds,struct dsa_notifier_mtu_info * info)70*4882a593Smuzhiyun static int dsa_switch_mtu(struct dsa_switch *ds,
71*4882a593Smuzhiyun 			  struct dsa_notifier_mtu_info *info)
72*4882a593Smuzhiyun {
73*4882a593Smuzhiyun 	int port, ret;
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	if (!ds->ops->port_change_mtu)
76*4882a593Smuzhiyun 		return -EOPNOTSUPP;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	for (port = 0; port < ds->num_ports; port++) {
79*4882a593Smuzhiyun 		if (dsa_switch_mtu_match(ds, port, info)) {
80*4882a593Smuzhiyun 			ret = ds->ops->port_change_mtu(ds, port, info->mtu);
81*4882a593Smuzhiyun 			if (ret)
82*4882a593Smuzhiyun 				return ret;
83*4882a593Smuzhiyun 		}
84*4882a593Smuzhiyun 	}
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	return 0;
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun 
dsa_switch_bridge_join(struct dsa_switch * ds,struct dsa_notifier_bridge_info * info)89*4882a593Smuzhiyun static int dsa_switch_bridge_join(struct dsa_switch *ds,
90*4882a593Smuzhiyun 				  struct dsa_notifier_bridge_info *info)
91*4882a593Smuzhiyun {
92*4882a593Smuzhiyun 	struct dsa_switch_tree *dst = ds->dst;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	if (dst->index == info->tree_index && ds->index == info->sw_index &&
95*4882a593Smuzhiyun 	    ds->ops->port_bridge_join)
96*4882a593Smuzhiyun 		return ds->ops->port_bridge_join(ds, info->port, info->br);
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	if ((dst->index != info->tree_index || ds->index != info->sw_index) &&
99*4882a593Smuzhiyun 	    ds->ops->crosschip_bridge_join)
100*4882a593Smuzhiyun 		return ds->ops->crosschip_bridge_join(ds, info->tree_index,
101*4882a593Smuzhiyun 						      info->sw_index,
102*4882a593Smuzhiyun 						      info->port, info->br);
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	return 0;
105*4882a593Smuzhiyun }
106*4882a593Smuzhiyun 
dsa_switch_bridge_leave(struct dsa_switch * ds,struct dsa_notifier_bridge_info * info)107*4882a593Smuzhiyun static int dsa_switch_bridge_leave(struct dsa_switch *ds,
108*4882a593Smuzhiyun 				   struct dsa_notifier_bridge_info *info)
109*4882a593Smuzhiyun {
110*4882a593Smuzhiyun 	bool unset_vlan_filtering = br_vlan_enabled(info->br);
111*4882a593Smuzhiyun 	struct dsa_switch_tree *dst = ds->dst;
112*4882a593Smuzhiyun 	int err, i;
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	if (dst->index == info->tree_index && ds->index == info->sw_index &&
115*4882a593Smuzhiyun 	    ds->ops->port_bridge_leave)
116*4882a593Smuzhiyun 		ds->ops->port_bridge_leave(ds, info->port, info->br);
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	if ((dst->index != info->tree_index || ds->index != info->sw_index) &&
119*4882a593Smuzhiyun 	    ds->ops->crosschip_bridge_leave)
120*4882a593Smuzhiyun 		ds->ops->crosschip_bridge_leave(ds, info->tree_index,
121*4882a593Smuzhiyun 						info->sw_index, info->port,
122*4882a593Smuzhiyun 						info->br);
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	/* If the bridge was vlan_filtering, the bridge core doesn't trigger an
125*4882a593Smuzhiyun 	 * event for changing vlan_filtering setting upon slave ports leaving
126*4882a593Smuzhiyun 	 * it. That is a good thing, because that lets us handle it and also
127*4882a593Smuzhiyun 	 * handle the case where the switch's vlan_filtering setting is global
128*4882a593Smuzhiyun 	 * (not per port). When that happens, the correct moment to trigger the
129*4882a593Smuzhiyun 	 * vlan_filtering callback is only when the last port left this bridge.
130*4882a593Smuzhiyun 	 */
131*4882a593Smuzhiyun 	if (unset_vlan_filtering && ds->vlan_filtering_is_global) {
132*4882a593Smuzhiyun 		for (i = 0; i < ds->num_ports; i++) {
133*4882a593Smuzhiyun 			if (i == info->port)
134*4882a593Smuzhiyun 				continue;
135*4882a593Smuzhiyun 			if (dsa_to_port(ds, i)->bridge_dev == info->br) {
136*4882a593Smuzhiyun 				unset_vlan_filtering = false;
137*4882a593Smuzhiyun 				break;
138*4882a593Smuzhiyun 			}
139*4882a593Smuzhiyun 		}
140*4882a593Smuzhiyun 	}
141*4882a593Smuzhiyun 	if (unset_vlan_filtering) {
142*4882a593Smuzhiyun 		struct switchdev_trans trans;
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 		trans.ph_prepare = true;
145*4882a593Smuzhiyun 		err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port),
146*4882a593Smuzhiyun 					      false, &trans);
147*4882a593Smuzhiyun 		if (err && err != EOPNOTSUPP)
148*4882a593Smuzhiyun 			return err;
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 		trans.ph_prepare = false;
151*4882a593Smuzhiyun 		err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port),
152*4882a593Smuzhiyun 					      false, &trans);
153*4882a593Smuzhiyun 		if (err && err != EOPNOTSUPP)
154*4882a593Smuzhiyun 			return err;
155*4882a593Smuzhiyun 	}
156*4882a593Smuzhiyun 	return 0;
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun 
dsa_switch_fdb_add(struct dsa_switch * ds,struct dsa_notifier_fdb_info * info)159*4882a593Smuzhiyun static int dsa_switch_fdb_add(struct dsa_switch *ds,
160*4882a593Smuzhiyun 			      struct dsa_notifier_fdb_info *info)
161*4882a593Smuzhiyun {
162*4882a593Smuzhiyun 	int port = dsa_towards_port(ds, info->sw_index, info->port);
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	if (!ds->ops->port_fdb_add)
165*4882a593Smuzhiyun 		return -EOPNOTSUPP;
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	return ds->ops->port_fdb_add(ds, port, info->addr, info->vid);
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun 
dsa_switch_fdb_del(struct dsa_switch * ds,struct dsa_notifier_fdb_info * info)170*4882a593Smuzhiyun static int dsa_switch_fdb_del(struct dsa_switch *ds,
171*4882a593Smuzhiyun 			      struct dsa_notifier_fdb_info *info)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun 	int port = dsa_towards_port(ds, info->sw_index, info->port);
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	if (!ds->ops->port_fdb_del)
176*4882a593Smuzhiyun 		return -EOPNOTSUPP;
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	return ds->ops->port_fdb_del(ds, port, info->addr, info->vid);
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun 
dsa_switch_mdb_match(struct dsa_switch * ds,int port,struct dsa_notifier_mdb_info * info)181*4882a593Smuzhiyun static bool dsa_switch_mdb_match(struct dsa_switch *ds, int port,
182*4882a593Smuzhiyun 				 struct dsa_notifier_mdb_info *info)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun 	if (ds->index == info->sw_index && port == info->port)
185*4882a593Smuzhiyun 		return true;
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	if (dsa_is_dsa_port(ds, port))
188*4882a593Smuzhiyun 		return true;
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	return false;
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun 
dsa_switch_mdb_prepare(struct dsa_switch * ds,struct dsa_notifier_mdb_info * info)193*4882a593Smuzhiyun static int dsa_switch_mdb_prepare(struct dsa_switch *ds,
194*4882a593Smuzhiyun 				  struct dsa_notifier_mdb_info *info)
195*4882a593Smuzhiyun {
196*4882a593Smuzhiyun 	int port, err;
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	if (!ds->ops->port_mdb_prepare || !ds->ops->port_mdb_add)
199*4882a593Smuzhiyun 		return -EOPNOTSUPP;
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	for (port = 0; port < ds->num_ports; port++) {
202*4882a593Smuzhiyun 		if (dsa_switch_mdb_match(ds, port, info)) {
203*4882a593Smuzhiyun 			err = ds->ops->port_mdb_prepare(ds, port, info->mdb);
204*4882a593Smuzhiyun 			if (err)
205*4882a593Smuzhiyun 				return err;
206*4882a593Smuzhiyun 		}
207*4882a593Smuzhiyun 	}
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	return 0;
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun 
dsa_switch_mdb_add(struct dsa_switch * ds,struct dsa_notifier_mdb_info * info)212*4882a593Smuzhiyun static int dsa_switch_mdb_add(struct dsa_switch *ds,
213*4882a593Smuzhiyun 			      struct dsa_notifier_mdb_info *info)
214*4882a593Smuzhiyun {
215*4882a593Smuzhiyun 	int port;
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	if (switchdev_trans_ph_prepare(info->trans))
218*4882a593Smuzhiyun 		return dsa_switch_mdb_prepare(ds, info);
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	if (!ds->ops->port_mdb_add)
221*4882a593Smuzhiyun 		return 0;
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	for (port = 0; port < ds->num_ports; port++)
224*4882a593Smuzhiyun 		if (dsa_switch_mdb_match(ds, port, info))
225*4882a593Smuzhiyun 			ds->ops->port_mdb_add(ds, port, info->mdb);
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	return 0;
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun 
dsa_switch_mdb_del(struct dsa_switch * ds,struct dsa_notifier_mdb_info * info)230*4882a593Smuzhiyun static int dsa_switch_mdb_del(struct dsa_switch *ds,
231*4882a593Smuzhiyun 			      struct dsa_notifier_mdb_info *info)
232*4882a593Smuzhiyun {
233*4882a593Smuzhiyun 	if (!ds->ops->port_mdb_del)
234*4882a593Smuzhiyun 		return -EOPNOTSUPP;
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	if (ds->index == info->sw_index)
237*4882a593Smuzhiyun 		return ds->ops->port_mdb_del(ds, info->port, info->mdb);
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	return 0;
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun 
dsa_switch_vlan_match(struct dsa_switch * ds,int port,struct dsa_notifier_vlan_info * info)242*4882a593Smuzhiyun static bool dsa_switch_vlan_match(struct dsa_switch *ds, int port,
243*4882a593Smuzhiyun 				  struct dsa_notifier_vlan_info *info)
244*4882a593Smuzhiyun {
245*4882a593Smuzhiyun 	if (ds->index == info->sw_index && port == info->port)
246*4882a593Smuzhiyun 		return true;
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	if (dsa_is_dsa_port(ds, port))
249*4882a593Smuzhiyun 		return true;
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	return false;
252*4882a593Smuzhiyun }
253*4882a593Smuzhiyun 
dsa_switch_vlan_prepare(struct dsa_switch * ds,struct dsa_notifier_vlan_info * info)254*4882a593Smuzhiyun static int dsa_switch_vlan_prepare(struct dsa_switch *ds,
255*4882a593Smuzhiyun 				   struct dsa_notifier_vlan_info *info)
256*4882a593Smuzhiyun {
257*4882a593Smuzhiyun 	int port, err;
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	if (!ds->ops->port_vlan_prepare || !ds->ops->port_vlan_add)
260*4882a593Smuzhiyun 		return -EOPNOTSUPP;
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	for (port = 0; port < ds->num_ports; port++) {
263*4882a593Smuzhiyun 		if (dsa_switch_vlan_match(ds, port, info)) {
264*4882a593Smuzhiyun 			err = ds->ops->port_vlan_prepare(ds, port, info->vlan);
265*4882a593Smuzhiyun 			if (err)
266*4882a593Smuzhiyun 				return err;
267*4882a593Smuzhiyun 		}
268*4882a593Smuzhiyun 	}
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 	return 0;
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun 
dsa_switch_vlan_add(struct dsa_switch * ds,struct dsa_notifier_vlan_info * info)273*4882a593Smuzhiyun static int dsa_switch_vlan_add(struct dsa_switch *ds,
274*4882a593Smuzhiyun 			       struct dsa_notifier_vlan_info *info)
275*4882a593Smuzhiyun {
276*4882a593Smuzhiyun 	int port;
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	if (switchdev_trans_ph_prepare(info->trans))
279*4882a593Smuzhiyun 		return dsa_switch_vlan_prepare(ds, info);
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	if (!ds->ops->port_vlan_add)
282*4882a593Smuzhiyun 		return 0;
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	for (port = 0; port < ds->num_ports; port++)
285*4882a593Smuzhiyun 		if (dsa_switch_vlan_match(ds, port, info))
286*4882a593Smuzhiyun 			ds->ops->port_vlan_add(ds, port, info->vlan);
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 	return 0;
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun 
dsa_switch_vlan_del(struct dsa_switch * ds,struct dsa_notifier_vlan_info * info)291*4882a593Smuzhiyun static int dsa_switch_vlan_del(struct dsa_switch *ds,
292*4882a593Smuzhiyun 			       struct dsa_notifier_vlan_info *info)
293*4882a593Smuzhiyun {
294*4882a593Smuzhiyun 	if (!ds->ops->port_vlan_del)
295*4882a593Smuzhiyun 		return -EOPNOTSUPP;
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 	if (ds->index == info->sw_index)
298*4882a593Smuzhiyun 		return ds->ops->port_vlan_del(ds, info->port, info->vlan);
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	/* Do not deprogram the DSA links as they may be used as conduit
301*4882a593Smuzhiyun 	 * for other VLAN members in the fabric.
302*4882a593Smuzhiyun 	 */
303*4882a593Smuzhiyun 	return 0;
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun 
dsa_switch_event(struct notifier_block * nb,unsigned long event,void * info)306*4882a593Smuzhiyun static int dsa_switch_event(struct notifier_block *nb,
307*4882a593Smuzhiyun 			    unsigned long event, void *info)
308*4882a593Smuzhiyun {
309*4882a593Smuzhiyun 	struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb);
310*4882a593Smuzhiyun 	int err;
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	switch (event) {
313*4882a593Smuzhiyun 	case DSA_NOTIFIER_AGEING_TIME:
314*4882a593Smuzhiyun 		err = dsa_switch_ageing_time(ds, info);
315*4882a593Smuzhiyun 		break;
316*4882a593Smuzhiyun 	case DSA_NOTIFIER_BRIDGE_JOIN:
317*4882a593Smuzhiyun 		err = dsa_switch_bridge_join(ds, info);
318*4882a593Smuzhiyun 		break;
319*4882a593Smuzhiyun 	case DSA_NOTIFIER_BRIDGE_LEAVE:
320*4882a593Smuzhiyun 		err = dsa_switch_bridge_leave(ds, info);
321*4882a593Smuzhiyun 		break;
322*4882a593Smuzhiyun 	case DSA_NOTIFIER_FDB_ADD:
323*4882a593Smuzhiyun 		err = dsa_switch_fdb_add(ds, info);
324*4882a593Smuzhiyun 		break;
325*4882a593Smuzhiyun 	case DSA_NOTIFIER_FDB_DEL:
326*4882a593Smuzhiyun 		err = dsa_switch_fdb_del(ds, info);
327*4882a593Smuzhiyun 		break;
328*4882a593Smuzhiyun 	case DSA_NOTIFIER_MDB_ADD:
329*4882a593Smuzhiyun 		err = dsa_switch_mdb_add(ds, info);
330*4882a593Smuzhiyun 		break;
331*4882a593Smuzhiyun 	case DSA_NOTIFIER_MDB_DEL:
332*4882a593Smuzhiyun 		err = dsa_switch_mdb_del(ds, info);
333*4882a593Smuzhiyun 		break;
334*4882a593Smuzhiyun 	case DSA_NOTIFIER_VLAN_ADD:
335*4882a593Smuzhiyun 		err = dsa_switch_vlan_add(ds, info);
336*4882a593Smuzhiyun 		break;
337*4882a593Smuzhiyun 	case DSA_NOTIFIER_VLAN_DEL:
338*4882a593Smuzhiyun 		err = dsa_switch_vlan_del(ds, info);
339*4882a593Smuzhiyun 		break;
340*4882a593Smuzhiyun 	case DSA_NOTIFIER_MTU:
341*4882a593Smuzhiyun 		err = dsa_switch_mtu(ds, info);
342*4882a593Smuzhiyun 		break;
343*4882a593Smuzhiyun 	default:
344*4882a593Smuzhiyun 		err = -EOPNOTSUPP;
345*4882a593Smuzhiyun 		break;
346*4882a593Smuzhiyun 	}
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	/* Non-switchdev operations cannot be rolled back. If a DSA driver
349*4882a593Smuzhiyun 	 * returns an error during the chained call, switch chips may be in an
350*4882a593Smuzhiyun 	 * inconsistent state.
351*4882a593Smuzhiyun 	 */
352*4882a593Smuzhiyun 	if (err)
353*4882a593Smuzhiyun 		dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n",
354*4882a593Smuzhiyun 			event, err);
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 	return notifier_from_errno(err);
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun 
dsa_switch_register_notifier(struct dsa_switch * ds)359*4882a593Smuzhiyun int dsa_switch_register_notifier(struct dsa_switch *ds)
360*4882a593Smuzhiyun {
361*4882a593Smuzhiyun 	ds->nb.notifier_call = dsa_switch_event;
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun 	return raw_notifier_chain_register(&ds->dst->nh, &ds->nb);
364*4882a593Smuzhiyun }
365*4882a593Smuzhiyun 
dsa_switch_unregister_notifier(struct dsa_switch * ds)366*4882a593Smuzhiyun void dsa_switch_unregister_notifier(struct dsa_switch *ds)
367*4882a593Smuzhiyun {
368*4882a593Smuzhiyun 	int err;
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun 	err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb);
371*4882a593Smuzhiyun 	if (err)
372*4882a593Smuzhiyun 		dev_err(ds->dev, "failed to unregister notifier (%d)\n", err);
373*4882a593Smuzhiyun }
374