blob: c1e5afafe63325290179aee08b5cbcba0c91c7f0 [file] [log] [blame]
Thomas Gleixner2874c5f2019-05-27 08:55:01 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Vivien Didelotf515f192017-02-03 13:20:20 -05002/*
3 * Handling of a single switch chip, part of a switch fabric
4 *
Vivien Didelot4333d612017-03-28 15:10:36 -04005 * Copyright (c) 2017 Savoir-faire Linux Inc.
6 * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Vivien Didelotf515f192017-02-03 13:20:20 -05007 */
8
Vladimir Olteand371b7c2019-04-28 21:45:46 +03009#include <linux/if_bridge.h>
Vivien Didelotf515f192017-02-03 13:20:20 -050010#include <linux/netdevice.h>
11#include <linux/notifier.h>
Florian Fainelli061f6a52019-02-20 14:35:39 -080012#include <linux/if_vlan.h>
Vivien Didelot1faabf72017-05-19 17:00:52 -040013#include <net/switchdev.h>
Vivien Didelotea5dd342017-05-17 15:46:03 -040014
15#include "dsa_priv.h"
Vivien Didelotf515f192017-02-03 13:20:20 -050016
Vivien Didelot1faabf72017-05-19 17:00:52 -040017static 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) {
Vivien Didelot68bb8ea2019-10-21 16:51:15 -040023 struct dsa_port *dp = dsa_to_port(ds, i);
Vivien Didelot1faabf72017-05-19 17:00:52 -040024
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;
Vivien Didelot1faabf72017-05-19 17:00:52 -040036
Vladimir Oltean77b61362021-01-09 02:01:51 +020037 if (ds->ageing_time_min && ageing_time < ds->ageing_time_min)
38 return -ERANGE;
39
40 if (ds->ageing_time_max && ageing_time > ds->ageing_time_max)
41 return -ERANGE;
Vivien Didelot1faabf72017-05-19 17:00:52 -040042
43 /* Program the fastest ageing time in case of multiple bridges */
44 ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time);
45
46 if (ds->ops->set_ageing_time)
47 return ds->ops->set_ageing_time(ds, ageing_time);
48
49 return 0;
50}
51
Vladimir Olteanbfcb8132020-03-27 21:55:42 +020052static bool dsa_switch_mtu_match(struct dsa_switch *ds, int port,
53 struct dsa_notifier_mtu_info *info)
54{
Vladimir Oltean88faba22021-06-21 19:42:18 +030055 if (ds->index == info->sw_index && port == info->port)
56 return true;
Vladimir Olteanbfcb8132020-03-27 21:55:42 +020057
Vladimir Oltean88faba22021-06-21 19:42:18 +030058 /* Do not propagate to other switches in the tree if the notifier was
59 * targeted for a single switch.
60 */
61 if (info->targeted_match)
Vladimir Olteanbfcb8132020-03-27 21:55:42 +020062 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
Vivien Didelot04d3a4c2017-02-03 13:20:21 -050089static int dsa_switch_bridge_join(struct dsa_switch *ds,
90 struct dsa_notifier_bridge_info *info)
91{
Vladimir Olteanf66a6a62020-05-10 19:37:41 +030092 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)
Vivien Didelot04d3a4c2017-02-03 13:20:21 -050096 return ds->ops->port_bridge_join(ds, info->port, info->br);
97
Vladimir Olteanf66a6a62020-05-10 19:37:41 +030098 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,
Vivien Didelot40ef2c92017-03-30 17:37:14 -0400102 info->port, info->br);
Vivien Didelot04d3a4c2017-02-03 13:20:21 -0500103
104 return 0;
105}
106
107static int dsa_switch_bridge_leave(struct dsa_switch *ds,
108 struct dsa_notifier_bridge_info *info)
109{
Vladimir Olteand371b7c2019-04-28 21:45:46 +0300110 bool unset_vlan_filtering = br_vlan_enabled(info->br);
Vladimir Olteanf66a6a62020-05-10 19:37:41 +0300111 struct dsa_switch_tree *dst = ds->dst;
Vladimir Oltean89153ed2021-02-13 22:43:19 +0200112 struct netlink_ext_ack extack = {0};
Vladimir Oltean479dc492021-03-24 11:56:39 +0200113 int err, port;
Vladimir Olteand371b7c2019-04-28 21:45:46 +0300114
Vladimir Olteanf66a6a62020-05-10 19:37:41 +0300115 if (dst->index == info->tree_index && ds->index == info->sw_index &&
116 ds->ops->port_bridge_join)
Vivien Didelot04d3a4c2017-02-03 13:20:21 -0500117 ds->ops->port_bridge_leave(ds, info->port, info->br);
118
Vladimir Olteanf66a6a62020-05-10 19:37:41 +0300119 if ((dst->index != info->tree_index || ds->index != info->sw_index) &&
120 ds->ops->crosschip_bridge_join)
121 ds->ops->crosschip_bridge_leave(ds, info->tree_index,
122 info->sw_index, info->port,
Vivien Didelot40ef2c92017-03-30 17:37:14 -0400123 info->br);
Vivien Didelot04d3a4c2017-02-03 13:20:21 -0500124
Vladimir Olteand371b7c2019-04-28 21:45:46 +0300125 /* If the bridge was vlan_filtering, the bridge core doesn't trigger an
126 * event for changing vlan_filtering setting upon slave ports leaving
127 * it. That is a good thing, because that lets us handle it and also
128 * handle the case where the switch's vlan_filtering setting is global
129 * (not per port). When that happens, the correct moment to trigger the
Vladimir Oltean479dc492021-03-24 11:56:39 +0200130 * vlan_filtering callback is only when the last port leaves the last
131 * VLAN-aware bridge.
Vladimir Olteand371b7c2019-04-28 21:45:46 +0300132 */
133 if (unset_vlan_filtering && ds->vlan_filtering_is_global) {
Vladimir Oltean479dc492021-03-24 11:56:39 +0200134 for (port = 0; port < ds->num_ports; port++) {
135 struct net_device *bridge_dev;
136
137 bridge_dev = dsa_to_port(ds, port)->bridge_dev;
138
139 if (bridge_dev && br_vlan_enabled(bridge_dev)) {
Vladimir Olteand371b7c2019-04-28 21:45:46 +0300140 unset_vlan_filtering = false;
141 break;
142 }
143 }
144 }
145 if (unset_vlan_filtering) {
Vladimir Oltean2e554a72020-10-03 01:06:46 +0300146 err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port),
Vladimir Oltean89153ed2021-02-13 22:43:19 +0200147 false, &extack);
148 if (extack._msg)
149 dev_err(ds->dev, "port %d: %s\n", info->port,
150 extack._msg);
Vladimir Olteand371b7c2019-04-28 21:45:46 +0300151 if (err && err != EOPNOTSUPP)
152 return err;
153 }
Vivien Didelot04d3a4c2017-02-03 13:20:21 -0500154 return 0;
155}
156
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400157static int dsa_switch_fdb_add(struct dsa_switch *ds,
158 struct dsa_notifier_fdb_info *info)
159{
Vivien Didelot31692412017-11-30 12:56:43 -0500160 int port = dsa_towards_port(ds, info->sw_index, info->port);
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400161
Arkadi Sharshevsky1b6dd552017-08-06 16:15:40 +0300162 if (!ds->ops->port_fdb_add)
163 return -EOPNOTSUPP;
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400164
Vivien Didelot31692412017-11-30 12:56:43 -0500165 return ds->ops->port_fdb_add(ds, port, info->addr, info->vid);
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400166}
167
168static int dsa_switch_fdb_del(struct dsa_switch *ds,
169 struct dsa_notifier_fdb_info *info)
170{
Vivien Didelot31692412017-11-30 12:56:43 -0500171 int port = dsa_towards_port(ds, info->sw_index, info->port);
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400172
173 if (!ds->ops->port_fdb_del)
174 return -EOPNOTSUPP;
175
Vivien Didelot31692412017-11-30 12:56:43 -0500176 return ds->ops->port_fdb_del(ds, port, info->addr, info->vid);
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400177}
178
George McCollister18596f52021-02-09 19:02:12 -0600179static int dsa_switch_hsr_join(struct dsa_switch *ds,
180 struct dsa_notifier_hsr_info *info)
181{
182 if (ds->index == info->sw_index && ds->ops->port_hsr_join)
183 return ds->ops->port_hsr_join(ds, info->port, info->hsr);
184
185 return -EOPNOTSUPP;
186}
187
188static int dsa_switch_hsr_leave(struct dsa_switch *ds,
189 struct dsa_notifier_hsr_info *info)
190{
191 if (ds->index == info->sw_index && ds->ops->port_hsr_leave)
192 return ds->ops->port_hsr_leave(ds, info->port, info->hsr);
193
194 return -EOPNOTSUPP;
195}
196
Tobias Waldekranz058102a2021-01-13 09:42:53 +0100197static int dsa_switch_lag_change(struct dsa_switch *ds,
198 struct dsa_notifier_lag_info *info)
199{
200 if (ds->index == info->sw_index && ds->ops->port_lag_change)
201 return ds->ops->port_lag_change(ds, info->port);
202
203 if (ds->index != info->sw_index && ds->ops->crosschip_lag_change)
204 return ds->ops->crosschip_lag_change(ds, info->sw_index,
205 info->port);
206
207 return 0;
208}
209
210static int dsa_switch_lag_join(struct dsa_switch *ds,
211 struct dsa_notifier_lag_info *info)
212{
213 if (ds->index == info->sw_index && ds->ops->port_lag_join)
214 return ds->ops->port_lag_join(ds, info->port, info->lag,
215 info->info);
216
217 if (ds->index != info->sw_index && ds->ops->crosschip_lag_join)
218 return ds->ops->crosschip_lag_join(ds, info->sw_index,
219 info->port, info->lag,
220 info->info);
221
222 return 0;
223}
224
225static int dsa_switch_lag_leave(struct dsa_switch *ds,
226 struct dsa_notifier_lag_info *info)
227{
228 if (ds->index == info->sw_index && ds->ops->port_lag_leave)
229 return ds->ops->port_lag_leave(ds, info->port, info->lag);
230
231 if (ds->index != info->sw_index && ds->ops->crosschip_lag_leave)
232 return ds->ops->crosschip_lag_leave(ds, info->sw_index,
233 info->port, info->lag);
234
235 return 0;
236}
237
Vladimir Olteanffb68fc2021-01-09 02:01:48 +0200238static int dsa_switch_mdb_add(struct dsa_switch *ds,
239 struct dsa_notifier_mdb_info *info)
Vivien Didelote6db98d2017-11-30 11:24:00 -0500240{
Vladimir Olteanabd49532021-06-21 19:42:16 +0300241 int port = dsa_towards_port(ds, info->sw_index, info->port);
Vivien Didelote6db98d2017-11-30 11:24:00 -0500242
Vladimir Olteana52b2da2021-01-09 02:01:52 +0200243 if (!ds->ops->port_mdb_add)
Vivien Didelote6db98d2017-11-30 11:24:00 -0500244 return -EOPNOTSUPP;
245
Vladimir Olteanabd49532021-06-21 19:42:16 +0300246 return ds->ops->port_mdb_add(ds, port, info->mdb);
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400247}
248
249static int dsa_switch_mdb_del(struct dsa_switch *ds,
250 struct dsa_notifier_mdb_info *info)
251{
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400252 if (!ds->ops->port_mdb_del)
253 return -EOPNOTSUPP;
254
Vivien Didelota1a6b7e2017-06-15 16:14:48 -0400255 if (ds->index == info->sw_index)
Vivien Didelote65d45c2019-08-25 13:25:15 -0400256 return ds->ops->port_mdb_del(ds, info->port, info->mdb);
Vivien Didelota1a6b7e2017-06-15 16:14:48 -0400257
258 return 0;
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400259}
260
Vivien Didelote65d45c2019-08-25 13:25:15 -0400261static bool dsa_switch_vlan_match(struct dsa_switch *ds, int port,
262 struct dsa_notifier_vlan_info *info)
263{
264 if (ds->index == info->sw_index && port == info->port)
265 return true;
266
Vivien Didelot7e1741b2019-08-25 13:25:19 -0400267 if (dsa_is_dsa_port(ds, port))
Vivien Didelote65d45c2019-08-25 13:25:15 -0400268 return true;
269
270 return false;
271}
272
Vladimir Olteanffb68fc2021-01-09 02:01:48 +0200273static int dsa_switch_vlan_add(struct dsa_switch *ds,
274 struct dsa_notifier_vlan_info *info)
Vivien Didelot9c428c52017-11-30 11:23:59 -0500275{
276 int port, err;
277
Vladimir Oltean1958d582021-01-09 02:01:53 +0200278 if (!ds->ops->port_vlan_add)
Vivien Didelot9c428c52017-11-30 11:23:59 -0500279 return -EOPNOTSUPP;
280
Vivien Didelote65d45c2019-08-25 13:25:15 -0400281 for (port = 0; port < ds->num_ports; port++) {
282 if (dsa_switch_vlan_match(ds, port, info)) {
Vladimir Oltean31046a52021-02-13 22:43:18 +0200283 err = ds->ops->port_vlan_add(ds, port, info->vlan,
284 info->extack);
Vivien Didelote65d45c2019-08-25 13:25:15 -0400285 if (err)
286 return err;
287 }
Vivien Didelot9c428c52017-11-30 11:23:59 -0500288 }
289
Vivien Didelotd0c627b2017-05-19 17:00:55 -0400290 return 0;
291}
292
293static int dsa_switch_vlan_del(struct dsa_switch *ds,
294 struct dsa_notifier_vlan_info *info)
295{
Vivien Didelotd0c627b2017-05-19 17:00:55 -0400296 if (!ds->ops->port_vlan_del)
297 return -EOPNOTSUPP;
298
Vivien Didelot1ca4aa92017-06-07 18:12:14 -0400299 if (ds->index == info->sw_index)
Vivien Didelote65d45c2019-08-25 13:25:15 -0400300 return ds->ops->port_vlan_del(ds, info->port, info->vlan);
Vivien Didelot1ca4aa92017-06-07 18:12:14 -0400301
Vivien Didelot7e1741b2019-08-25 13:25:19 -0400302 /* Do not deprogram the DSA links as they may be used as conduit
303 * for other VLAN members in the fabric.
304 */
Vivien Didelot1ca4aa92017-06-07 18:12:14 -0400305 return 0;
Vivien Didelotd0c627b2017-05-19 17:00:55 -0400306}
307
Vladimir Oltean53da0eb2021-01-29 03:00:06 +0200308static int dsa_switch_change_tag_proto(struct dsa_switch *ds,
309 struct dsa_notifier_tag_proto_info *info)
310{
311 const struct dsa_device_ops *tag_ops = info->tag_ops;
312 int port, err;
313
314 if (!ds->ops->change_tag_protocol)
315 return -EOPNOTSUPP;
316
317 ASSERT_RTNL();
318
319 for (port = 0; port < ds->num_ports; port++) {
Tobias Waldekranz21e0b502021-04-20 20:53:09 +0200320 if (!dsa_is_cpu_port(ds, port))
321 continue;
Vladimir Oltean53da0eb2021-01-29 03:00:06 +0200322
Tobias Waldekranz21e0b502021-04-20 20:53:09 +0200323 err = ds->ops->change_tag_protocol(ds, port, tag_ops->proto);
324 if (err)
325 return err;
326
327 dsa_port_set_tag_protocol(dsa_to_port(ds, port), tag_ops);
Vladimir Oltean53da0eb2021-01-29 03:00:06 +0200328 }
329
330 /* Now that changing the tag protocol can no longer fail, let's update
331 * the remaining bits which are "duplicated for faster access", and the
332 * bits that depend on the tagger, such as the MTU.
333 */
334 for (port = 0; port < ds->num_ports; port++) {
335 if (dsa_is_user_port(ds, port)) {
336 struct net_device *slave;
337
338 slave = dsa_to_port(ds, port)->slave;
339 dsa_slave_setup_tagger(slave);
340
341 /* rtnl_mutex is held in dsa_tree_change_tag_proto */
342 dsa_slave_change_mtu(slave, slave->mtu);
343 }
344 }
345
346 return 0;
347}
348
Horatiu Vulturc595c432021-02-16 22:42:04 +0100349static int dsa_switch_mrp_add(struct dsa_switch *ds,
350 struct dsa_notifier_mrp_info *info)
351{
Horatiu Vulturc595c432021-02-16 22:42:04 +0100352 if (!ds->ops->port_mrp_add)
353 return -EOPNOTSUPP;
354
Vladimir Olteanf9bcdc362021-06-21 19:42:19 +0300355 if (ds->index == info->sw_index)
356 return ds->ops->port_mrp_add(ds, info->port, info->mrp);
Horatiu Vulturc595c432021-02-16 22:42:04 +0100357
Vladimir Olteanf9bcdc362021-06-21 19:42:19 +0300358 return 0;
Horatiu Vulturc595c432021-02-16 22:42:04 +0100359}
360
361static int dsa_switch_mrp_del(struct dsa_switch *ds,
362 struct dsa_notifier_mrp_info *info)
363{
364 if (!ds->ops->port_mrp_del)
365 return -EOPNOTSUPP;
366
367 if (ds->index == info->sw_index)
368 return ds->ops->port_mrp_del(ds, info->port, info->mrp);
369
370 return 0;
371}
372
Horatiu Vulturc595c432021-02-16 22:42:04 +0100373static int
374dsa_switch_mrp_add_ring_role(struct dsa_switch *ds,
375 struct dsa_notifier_mrp_ring_role_info *info)
376{
Horatiu Vulturc595c432021-02-16 22:42:04 +0100377 if (!ds->ops->port_mrp_add)
378 return -EOPNOTSUPP;
379
Vladimir Olteanf9bcdc362021-06-21 19:42:19 +0300380 if (ds->index == info->sw_index)
381 return ds->ops->port_mrp_add_ring_role(ds, info->port,
382 info->mrp);
Horatiu Vulturc595c432021-02-16 22:42:04 +0100383
Vladimir Olteanf9bcdc362021-06-21 19:42:19 +0300384 return 0;
Horatiu Vulturc595c432021-02-16 22:42:04 +0100385}
386
387static int
388dsa_switch_mrp_del_ring_role(struct dsa_switch *ds,
389 struct dsa_notifier_mrp_ring_role_info *info)
390{
391 if (!ds->ops->port_mrp_del)
392 return -EOPNOTSUPP;
393
394 if (ds->index == info->sw_index)
395 return ds->ops->port_mrp_del_ring_role(ds, info->port,
396 info->mrp);
397
398 return 0;
399}
400
Vivien Didelotf515f192017-02-03 13:20:20 -0500401static int dsa_switch_event(struct notifier_block *nb,
402 unsigned long event, void *info)
403{
404 struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb);
405 int err;
406
407 switch (event) {
Vivien Didelot1faabf72017-05-19 17:00:52 -0400408 case DSA_NOTIFIER_AGEING_TIME:
409 err = dsa_switch_ageing_time(ds, info);
410 break;
Vivien Didelot04d3a4c2017-02-03 13:20:21 -0500411 case DSA_NOTIFIER_BRIDGE_JOIN:
412 err = dsa_switch_bridge_join(ds, info);
413 break;
414 case DSA_NOTIFIER_BRIDGE_LEAVE:
415 err = dsa_switch_bridge_leave(ds, info);
416 break;
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400417 case DSA_NOTIFIER_FDB_ADD:
418 err = dsa_switch_fdb_add(ds, info);
419 break;
420 case DSA_NOTIFIER_FDB_DEL:
421 err = dsa_switch_fdb_del(ds, info);
422 break;
George McCollister18596f52021-02-09 19:02:12 -0600423 case DSA_NOTIFIER_HSR_JOIN:
424 err = dsa_switch_hsr_join(ds, info);
425 break;
426 case DSA_NOTIFIER_HSR_LEAVE:
427 err = dsa_switch_hsr_leave(ds, info);
428 break;
Tobias Waldekranz058102a2021-01-13 09:42:53 +0100429 case DSA_NOTIFIER_LAG_CHANGE:
430 err = dsa_switch_lag_change(ds, info);
431 break;
432 case DSA_NOTIFIER_LAG_JOIN:
433 err = dsa_switch_lag_join(ds, info);
434 break;
435 case DSA_NOTIFIER_LAG_LEAVE:
436 err = dsa_switch_lag_leave(ds, info);
437 break;
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400438 case DSA_NOTIFIER_MDB_ADD:
439 err = dsa_switch_mdb_add(ds, info);
440 break;
441 case DSA_NOTIFIER_MDB_DEL:
442 err = dsa_switch_mdb_del(ds, info);
443 break;
Vivien Didelotd0c627b2017-05-19 17:00:55 -0400444 case DSA_NOTIFIER_VLAN_ADD:
445 err = dsa_switch_vlan_add(ds, info);
446 break;
447 case DSA_NOTIFIER_VLAN_DEL:
448 err = dsa_switch_vlan_del(ds, info);
449 break;
Vladimir Olteanbfcb8132020-03-27 21:55:42 +0200450 case DSA_NOTIFIER_MTU:
451 err = dsa_switch_mtu(ds, info);
452 break;
Vladimir Oltean53da0eb2021-01-29 03:00:06 +0200453 case DSA_NOTIFIER_TAG_PROTO:
454 err = dsa_switch_change_tag_proto(ds, info);
455 break;
Horatiu Vulturc595c432021-02-16 22:42:04 +0100456 case DSA_NOTIFIER_MRP_ADD:
457 err = dsa_switch_mrp_add(ds, info);
458 break;
459 case DSA_NOTIFIER_MRP_DEL:
460 err = dsa_switch_mrp_del(ds, info);
461 break;
462 case DSA_NOTIFIER_MRP_ADD_RING_ROLE:
463 err = dsa_switch_mrp_add_ring_role(ds, info);
464 break;
465 case DSA_NOTIFIER_MRP_DEL_RING_ROLE:
466 err = dsa_switch_mrp_del_ring_role(ds, info);
467 break;
Vivien Didelotf515f192017-02-03 13:20:20 -0500468 default:
469 err = -EOPNOTSUPP;
470 break;
471 }
472
Vivien Didelotf515f192017-02-03 13:20:20 -0500473 if (err)
474 dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n",
475 event, err);
476
477 return notifier_from_errno(err);
478}
479
480int dsa_switch_register_notifier(struct dsa_switch *ds)
481{
482 ds->nb.notifier_call = dsa_switch_event;
483
484 return raw_notifier_chain_register(&ds->dst->nh, &ds->nb);
485}
486
487void dsa_switch_unregister_notifier(struct dsa_switch *ds)
488{
489 int err;
490
491 err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb);
492 if (err)
493 dev_err(ds->dev, "failed to unregister notifier (%d)\n", err);
494}