Linux Audio

Check our new training course

Yocto distribution development and maintenance

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