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 { 20d0004a02SVladimir Oltean struct dsa_port *dp; 211faabf74SVivien Didelot 22d0004a02SVladimir Oltean dsa_switch_for_each_port(dp, ds) 231faabf74SVivien Didelot if (dp->ageing_time && dp->ageing_time < ageing_time) 241faabf74SVivien Didelot ageing_time = dp->ageing_time; 251faabf74SVivien Didelot 261faabf74SVivien Didelot return ageing_time; 271faabf74SVivien Didelot } 281faabf74SVivien Didelot 291faabf74SVivien Didelot static int dsa_switch_ageing_time(struct dsa_switch *ds, 301faabf74SVivien Didelot struct dsa_notifier_ageing_time_info *info) 311faabf74SVivien Didelot { 321faabf74SVivien Didelot unsigned int ageing_time = info->ageing_time; 331faabf74SVivien Didelot 341faabf74SVivien Didelot if (ds->ageing_time_min && ageing_time < ds->ageing_time_min) 351faabf74SVivien Didelot return -ERANGE; 3677b61365SVladimir Oltean 371faabf74SVivien Didelot if (ds->ageing_time_max && ageing_time > ds->ageing_time_max) 381faabf74SVivien Didelot return -ERANGE; 391faabf74SVivien Didelot 401faabf74SVivien Didelot /* Program the fastest ageing time in case of multiple bridges */ 411faabf74SVivien Didelot ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time); 421faabf74SVivien Didelot 431faabf74SVivien Didelot if (ds->ops->set_ageing_time) 441faabf74SVivien Didelot return ds->ops->set_ageing_time(ds, ageing_time); 451faabf74SVivien Didelot 461faabf74SVivien Didelot return 0; 471faabf74SVivien Didelot } 481faabf74SVivien Didelot 49fac6abd5SVladimir Oltean static bool dsa_port_mtu_match(struct dsa_port *dp, 50bfcb8132SVladimir Oltean struct dsa_notifier_mtu_info *info) 51bfcb8132SVladimir Oltean { 52fac6abd5SVladimir Oltean if (dp->ds->index == info->sw_index && dp->index == info->port) 5388faba20SVladimir Oltean return true; 54bfcb8132SVladimir Oltean 5588faba20SVladimir Oltean /* Do not propagate to other switches in the tree if the notifier was 5688faba20SVladimir Oltean * targeted for a single switch. 5788faba20SVladimir Oltean */ 5888faba20SVladimir Oltean if (info->targeted_match) 59bfcb8132SVladimir Oltean return false; 60bfcb8132SVladimir Oltean 61fac6abd5SVladimir Oltean if (dsa_port_is_dsa(dp) || dsa_port_is_cpu(dp)) 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 { 70fac6abd5SVladimir Oltean struct dsa_port *dp; 71fac6abd5SVladimir Oltean int ret; 72bfcb8132SVladimir Oltean 73bfcb8132SVladimir Oltean if (!ds->ops->port_change_mtu) 74bfcb8132SVladimir Oltean return -EOPNOTSUPP; 75bfcb8132SVladimir Oltean 76fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 77fac6abd5SVladimir Oltean if (dsa_port_mtu_match(dp, info)) { 78fac6abd5SVladimir Oltean ret = ds->ops->port_change_mtu(ds, dp->index, 79fac6abd5SVladimir Oltean info->mtu); 80bfcb8132SVladimir Oltean if (ret) 81bfcb8132SVladimir Oltean return ret; 82bfcb8132SVladimir Oltean } 83bfcb8132SVladimir Oltean } 84bfcb8132SVladimir Oltean 85bfcb8132SVladimir Oltean return 0; 86bfcb8132SVladimir Oltean } 87bfcb8132SVladimir Oltean 8804d3a4c6SVivien Didelot static int dsa_switch_bridge_join(struct dsa_switch *ds, 8904d3a4c6SVivien Didelot struct dsa_notifier_bridge_info *info) 9004d3a4c6SVivien Didelot { 91f66a6a69SVladimir Oltean struct dsa_switch_tree *dst = ds->dst; 92e19cc13cSVladimir Oltean int err; 93f66a6a69SVladimir Oltean 9467b5fb5dSVladimir Oltean if (dst->index == info->tree_index && ds->index == info->sw_index) { 9567b5fb5dSVladimir Oltean if (!ds->ops->port_bridge_join) 9667b5fb5dSVladimir Oltean return -EOPNOTSUPP; 9767b5fb5dSVladimir Oltean 98e19cc13cSVladimir Oltean err = ds->ops->port_bridge_join(ds, info->port, info->br); 99e19cc13cSVladimir Oltean if (err) 100e19cc13cSVladimir Oltean return err; 101e19cc13cSVladimir Oltean } 10204d3a4c6SVivien Didelot 103f66a6a69SVladimir Oltean if ((dst->index != info->tree_index || ds->index != info->sw_index) && 104e19cc13cSVladimir Oltean ds->ops->crosschip_bridge_join) { 105e19cc13cSVladimir Oltean err = ds->ops->crosschip_bridge_join(ds, info->tree_index, 106f66a6a69SVladimir Oltean info->sw_index, 10740ef2c93SVivien Didelot info->port, info->br); 108e19cc13cSVladimir Oltean if (err) 109e19cc13cSVladimir Oltean return err; 110e19cc13cSVladimir Oltean } 11104d3a4c6SVivien Didelot 112e19cc13cSVladimir Oltean return dsa_tag_8021q_bridge_join(ds, info); 11304d3a4c6SVivien Didelot } 11404d3a4c6SVivien Didelot 11504d3a4c6SVivien Didelot static int dsa_switch_bridge_leave(struct dsa_switch *ds, 11604d3a4c6SVivien Didelot struct dsa_notifier_bridge_info *info) 11704d3a4c6SVivien Didelot { 118f66a6a69SVladimir Oltean struct dsa_switch_tree *dst = ds->dst; 11989153ed6SVladimir Oltean struct netlink_ext_ack extack = {0}; 12058adf9dcSVladimir Oltean bool change_vlan_filtering = false; 12158adf9dcSVladimir Oltean bool vlan_filtering; 122d0004a02SVladimir Oltean struct dsa_port *dp; 123d0004a02SVladimir Oltean int err; 124d371b7c9SVladimir Oltean 125f66a6a69SVladimir Oltean if (dst->index == info->tree_index && ds->index == info->sw_index && 126bcb9928aSVladimir Oltean ds->ops->port_bridge_leave) 12704d3a4c6SVivien Didelot ds->ops->port_bridge_leave(ds, info->port, info->br); 12804d3a4c6SVivien Didelot 129f66a6a69SVladimir Oltean if ((dst->index != info->tree_index || ds->index != info->sw_index) && 130bcb9928aSVladimir Oltean ds->ops->crosschip_bridge_leave) 131f66a6a69SVladimir Oltean ds->ops->crosschip_bridge_leave(ds, info->tree_index, 132f66a6a69SVladimir Oltean info->sw_index, info->port, 13340ef2c93SVivien Didelot info->br); 13404d3a4c6SVivien Didelot 13558adf9dcSVladimir Oltean if (ds->needs_standalone_vlan_filtering && !br_vlan_enabled(info->br)) { 13658adf9dcSVladimir Oltean change_vlan_filtering = true; 13758adf9dcSVladimir Oltean vlan_filtering = true; 13858adf9dcSVladimir Oltean } else if (!ds->needs_standalone_vlan_filtering && 13958adf9dcSVladimir Oltean br_vlan_enabled(info->br)) { 14058adf9dcSVladimir Oltean change_vlan_filtering = true; 14158adf9dcSVladimir Oltean vlan_filtering = false; 14258adf9dcSVladimir Oltean } 14358adf9dcSVladimir Oltean 144d371b7c9SVladimir Oltean /* If the bridge was vlan_filtering, the bridge core doesn't trigger an 145d371b7c9SVladimir Oltean * event for changing vlan_filtering setting upon slave ports leaving 146d371b7c9SVladimir Oltean * it. That is a good thing, because that lets us handle it and also 147d371b7c9SVladimir Oltean * handle the case where the switch's vlan_filtering setting is global 148d371b7c9SVladimir Oltean * (not per port). When that happens, the correct moment to trigger the 149479dc497SVladimir Oltean * vlan_filtering callback is only when the last port leaves the last 150479dc497SVladimir Oltean * VLAN-aware bridge. 151d371b7c9SVladimir Oltean */ 15258adf9dcSVladimir Oltean if (change_vlan_filtering && ds->vlan_filtering_is_global) { 153d0004a02SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 154479dc497SVladimir Oltean struct net_device *bridge_dev; 155479dc497SVladimir Oltean 156d0004a02SVladimir Oltean bridge_dev = dp->bridge_dev; 157479dc497SVladimir Oltean 158479dc497SVladimir Oltean if (bridge_dev && br_vlan_enabled(bridge_dev)) { 15958adf9dcSVladimir Oltean change_vlan_filtering = false; 160d371b7c9SVladimir Oltean break; 161d371b7c9SVladimir Oltean } 162d371b7c9SVladimir Oltean } 163d371b7c9SVladimir Oltean } 16458adf9dcSVladimir Oltean 16558adf9dcSVladimir Oltean if (change_vlan_filtering) { 1662e554a7aSVladimir Oltean err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port), 16758adf9dcSVladimir Oltean vlan_filtering, &extack); 16889153ed6SVladimir Oltean if (extack._msg) 16989153ed6SVladimir Oltean dev_err(ds->dev, "port %d: %s\n", info->port, 17089153ed6SVladimir Oltean extack._msg); 17143a4b4dbSAlvin Šipraga if (err && err != -EOPNOTSUPP) 172d371b7c9SVladimir Oltean return err; 173d371b7c9SVladimir Oltean } 174e19cc13cSVladimir Oltean 175e19cc13cSVladimir Oltean return dsa_tag_8021q_bridge_leave(ds, info); 17604d3a4c6SVivien Didelot } 17704d3a4c6SVivien Didelot 178b8e997c4SVladimir Oltean /* Matches for all upstream-facing ports (the CPU port and all upstream-facing 179b8e997c4SVladimir Oltean * DSA links) that sit between the targeted port on which the notifier was 180b8e997c4SVladimir Oltean * emitted and its dedicated CPU port. 181b8e997c4SVladimir Oltean */ 182fac6abd5SVladimir Oltean static bool dsa_port_host_address_match(struct dsa_port *dp, 183b8e997c4SVladimir Oltean int info_sw_index, int info_port) 184b8e997c4SVladimir Oltean { 185b8e997c4SVladimir Oltean struct dsa_port *targeted_dp, *cpu_dp; 186b8e997c4SVladimir Oltean struct dsa_switch *targeted_ds; 187b8e997c4SVladimir Oltean 188fac6abd5SVladimir Oltean targeted_ds = dsa_switch_find(dp->ds->dst->index, info_sw_index); 189b8e997c4SVladimir Oltean targeted_dp = dsa_to_port(targeted_ds, info_port); 190b8e997c4SVladimir Oltean cpu_dp = targeted_dp->cpu_dp; 191b8e997c4SVladimir Oltean 192fac6abd5SVladimir Oltean if (dsa_switch_is_upstream_of(dp->ds, targeted_ds)) 193fac6abd5SVladimir Oltean return dp->index == dsa_towards_port(dp->ds, cpu_dp->ds->index, 194b8e997c4SVladimir Oltean cpu_dp->index); 195b8e997c4SVladimir Oltean 196b8e997c4SVladimir Oltean return false; 197b8e997c4SVladimir Oltean } 198b8e997c4SVladimir Oltean 199161ca59dSVladimir Oltean static struct dsa_mac_addr *dsa_mac_addr_find(struct list_head *addr_list, 200161ca59dSVladimir Oltean const unsigned char *addr, 201161ca59dSVladimir Oltean u16 vid) 202161ca59dSVladimir Oltean { 203161ca59dSVladimir Oltean struct dsa_mac_addr *a; 204161ca59dSVladimir Oltean 205161ca59dSVladimir Oltean list_for_each_entry(a, addr_list, list) 206161ca59dSVladimir Oltean if (ether_addr_equal(a->addr, addr) && a->vid == vid) 207161ca59dSVladimir Oltean return a; 208161ca59dSVladimir Oltean 209161ca59dSVladimir Oltean return NULL; 210161ca59dSVladimir Oltean } 211161ca59dSVladimir Oltean 212fac6abd5SVladimir Oltean static int dsa_port_do_mdb_add(struct dsa_port *dp, 213161ca59dSVladimir Oltean const struct switchdev_obj_port_mdb *mdb) 214161ca59dSVladimir Oltean { 215fac6abd5SVladimir Oltean struct dsa_switch *ds = dp->ds; 216161ca59dSVladimir Oltean struct dsa_mac_addr *a; 217fac6abd5SVladimir Oltean int port = dp->index; 2182d7e73f0SDavid S. Miller int err; 219161ca59dSVladimir Oltean 220161ca59dSVladimir Oltean /* No need to bother with refcounting for user ports */ 221161ca59dSVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 222161ca59dSVladimir Oltean return ds->ops->port_mdb_add(ds, port, mdb); 223161ca59dSVladimir Oltean 224161ca59dSVladimir Oltean a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid); 225161ca59dSVladimir Oltean if (a) { 226161ca59dSVladimir Oltean refcount_inc(&a->refcount); 2272d7e73f0SDavid S. Miller return 0; 228161ca59dSVladimir Oltean } 229161ca59dSVladimir Oltean 230161ca59dSVladimir Oltean a = kzalloc(sizeof(*a), GFP_KERNEL); 2312d7e73f0SDavid S. Miller if (!a) 2322d7e73f0SDavid S. Miller return -ENOMEM; 233161ca59dSVladimir Oltean 234161ca59dSVladimir Oltean err = ds->ops->port_mdb_add(ds, port, mdb); 235161ca59dSVladimir Oltean if (err) { 236161ca59dSVladimir Oltean kfree(a); 2372d7e73f0SDavid S. Miller return err; 238161ca59dSVladimir Oltean } 239161ca59dSVladimir Oltean 240161ca59dSVladimir Oltean ether_addr_copy(a->addr, mdb->addr); 241161ca59dSVladimir Oltean a->vid = mdb->vid; 242161ca59dSVladimir Oltean refcount_set(&a->refcount, 1); 243161ca59dSVladimir Oltean list_add_tail(&a->list, &dp->mdbs); 244161ca59dSVladimir Oltean 2452d7e73f0SDavid S. Miller return 0; 246161ca59dSVladimir Oltean } 247161ca59dSVladimir Oltean 248fac6abd5SVladimir Oltean static int dsa_port_do_mdb_del(struct dsa_port *dp, 249161ca59dSVladimir Oltean const struct switchdev_obj_port_mdb *mdb) 250161ca59dSVladimir Oltean { 251fac6abd5SVladimir Oltean struct dsa_switch *ds = dp->ds; 252161ca59dSVladimir Oltean struct dsa_mac_addr *a; 253fac6abd5SVladimir Oltean int port = dp->index; 2542d7e73f0SDavid S. Miller int err; 255161ca59dSVladimir Oltean 256161ca59dSVladimir Oltean /* No need to bother with refcounting for user ports */ 257161ca59dSVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 258161ca59dSVladimir Oltean return ds->ops->port_mdb_del(ds, port, mdb); 259161ca59dSVladimir Oltean 260161ca59dSVladimir Oltean a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid); 2612d7e73f0SDavid S. Miller if (!a) 2622d7e73f0SDavid S. Miller return -ENOENT; 263161ca59dSVladimir Oltean 264161ca59dSVladimir Oltean if (!refcount_dec_and_test(&a->refcount)) 2652d7e73f0SDavid S. Miller return 0; 266161ca59dSVladimir Oltean 267161ca59dSVladimir Oltean err = ds->ops->port_mdb_del(ds, port, mdb); 268161ca59dSVladimir Oltean if (err) { 269*232deb3fSVladimir Oltean refcount_set(&a->refcount, 1); 2702d7e73f0SDavid S. Miller return err; 271161ca59dSVladimir Oltean } 272161ca59dSVladimir Oltean 273161ca59dSVladimir Oltean list_del(&a->list); 274161ca59dSVladimir Oltean kfree(a); 275161ca59dSVladimir Oltean 2762d7e73f0SDavid S. Miller return 0; 277161ca59dSVladimir Oltean } 278161ca59dSVladimir Oltean 279fac6abd5SVladimir Oltean static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr, 280fac6abd5SVladimir Oltean u16 vid) 2813f6e32f9SVladimir Oltean { 282fac6abd5SVladimir Oltean struct dsa_switch *ds = dp->ds; 2833f6e32f9SVladimir Oltean struct dsa_mac_addr *a; 284fac6abd5SVladimir Oltean int port = dp->index; 2852d7e73f0SDavid S. Miller int err; 2863f6e32f9SVladimir Oltean 2873f6e32f9SVladimir Oltean /* No need to bother with refcounting for user ports */ 2883f6e32f9SVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 2893f6e32f9SVladimir Oltean return ds->ops->port_fdb_add(ds, port, addr, vid); 2903f6e32f9SVladimir Oltean 2913f6e32f9SVladimir Oltean a = dsa_mac_addr_find(&dp->fdbs, addr, vid); 2923f6e32f9SVladimir Oltean if (a) { 2933f6e32f9SVladimir Oltean refcount_inc(&a->refcount); 2942d7e73f0SDavid S. Miller return 0; 2953f6e32f9SVladimir Oltean } 2963f6e32f9SVladimir Oltean 2973f6e32f9SVladimir Oltean a = kzalloc(sizeof(*a), GFP_KERNEL); 2982d7e73f0SDavid S. Miller if (!a) 2992d7e73f0SDavid S. Miller return -ENOMEM; 3003f6e32f9SVladimir Oltean 3013f6e32f9SVladimir Oltean err = ds->ops->port_fdb_add(ds, port, addr, vid); 3023f6e32f9SVladimir Oltean if (err) { 3033f6e32f9SVladimir Oltean kfree(a); 3042d7e73f0SDavid S. Miller return err; 3053f6e32f9SVladimir Oltean } 3063f6e32f9SVladimir Oltean 3073f6e32f9SVladimir Oltean ether_addr_copy(a->addr, addr); 3083f6e32f9SVladimir Oltean a->vid = vid; 3093f6e32f9SVladimir Oltean refcount_set(&a->refcount, 1); 3103f6e32f9SVladimir Oltean list_add_tail(&a->list, &dp->fdbs); 3113f6e32f9SVladimir Oltean 3122d7e73f0SDavid S. Miller return 0; 3133f6e32f9SVladimir Oltean } 3143f6e32f9SVladimir Oltean 315fac6abd5SVladimir Oltean static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr, 316fac6abd5SVladimir Oltean u16 vid) 3173f6e32f9SVladimir Oltean { 318fac6abd5SVladimir Oltean struct dsa_switch *ds = dp->ds; 3193f6e32f9SVladimir Oltean struct dsa_mac_addr *a; 320fac6abd5SVladimir Oltean int port = dp->index; 3212d7e73f0SDavid S. Miller int err; 3223f6e32f9SVladimir Oltean 3233f6e32f9SVladimir Oltean /* No need to bother with refcounting for user ports */ 3243f6e32f9SVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 3253f6e32f9SVladimir Oltean return ds->ops->port_fdb_del(ds, port, addr, vid); 3263f6e32f9SVladimir Oltean 3273f6e32f9SVladimir Oltean a = dsa_mac_addr_find(&dp->fdbs, addr, vid); 3282d7e73f0SDavid S. Miller if (!a) 3292d7e73f0SDavid S. Miller return -ENOENT; 3303f6e32f9SVladimir Oltean 3313f6e32f9SVladimir Oltean if (!refcount_dec_and_test(&a->refcount)) 3322d7e73f0SDavid S. Miller return 0; 3333f6e32f9SVladimir Oltean 3343f6e32f9SVladimir Oltean err = ds->ops->port_fdb_del(ds, port, addr, vid); 3353f6e32f9SVladimir Oltean if (err) { 336*232deb3fSVladimir Oltean refcount_set(&a->refcount, 1); 3372d7e73f0SDavid S. Miller return err; 3383f6e32f9SVladimir Oltean } 3393f6e32f9SVladimir Oltean 3403f6e32f9SVladimir Oltean list_del(&a->list); 3413f6e32f9SVladimir Oltean kfree(a); 3423f6e32f9SVladimir Oltean 3432d7e73f0SDavid S. Miller return 0; 3443f6e32f9SVladimir Oltean } 3453f6e32f9SVladimir Oltean 3463dc80afcSVladimir Oltean static int dsa_switch_host_fdb_add(struct dsa_switch *ds, 3473dc80afcSVladimir Oltean struct dsa_notifier_fdb_info *info) 3483dc80afcSVladimir Oltean { 349fac6abd5SVladimir Oltean struct dsa_port *dp; 3503dc80afcSVladimir Oltean int err = 0; 3513dc80afcSVladimir Oltean 3523dc80afcSVladimir Oltean if (!ds->ops->port_fdb_add) 3533dc80afcSVladimir Oltean return -EOPNOTSUPP; 3543dc80afcSVladimir Oltean 355fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 356fac6abd5SVladimir Oltean if (dsa_port_host_address_match(dp, info->sw_index, 3573dc80afcSVladimir Oltean info->port)) { 358fac6abd5SVladimir Oltean err = dsa_port_do_fdb_add(dp, info->addr, info->vid); 3593dc80afcSVladimir Oltean if (err) 3603dc80afcSVladimir Oltean break; 3613dc80afcSVladimir Oltean } 3623dc80afcSVladimir Oltean } 3633dc80afcSVladimir Oltean 3643dc80afcSVladimir Oltean return err; 3653dc80afcSVladimir Oltean } 3663dc80afcSVladimir Oltean 3673dc80afcSVladimir Oltean static int dsa_switch_host_fdb_del(struct dsa_switch *ds, 3683dc80afcSVladimir Oltean struct dsa_notifier_fdb_info *info) 3693dc80afcSVladimir Oltean { 370fac6abd5SVladimir Oltean struct dsa_port *dp; 3713f6e32f9SVladimir Oltean int err = 0; 3723f6e32f9SVladimir Oltean 3733dc80afcSVladimir Oltean if (!ds->ops->port_fdb_del) 3743dc80afcSVladimir Oltean return -EOPNOTSUPP; 3753dc80afcSVladimir Oltean 376fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 377fac6abd5SVladimir Oltean if (dsa_port_host_address_match(dp, info->sw_index, 3783f6e32f9SVladimir Oltean info->port)) { 379fac6abd5SVladimir Oltean err = dsa_port_do_fdb_del(dp, info->addr, info->vid); 3803f6e32f9SVladimir Oltean if (err) 3813f6e32f9SVladimir Oltean break; 3823f6e32f9SVladimir Oltean } 3833f6e32f9SVladimir Oltean } 3843dc80afcSVladimir Oltean 3853f6e32f9SVladimir Oltean return err; 3863dc80afcSVladimir Oltean } 3873dc80afcSVladimir Oltean 388685fb6a4SVivien Didelot static int dsa_switch_fdb_add(struct dsa_switch *ds, 389685fb6a4SVivien Didelot struct dsa_notifier_fdb_info *info) 390685fb6a4SVivien Didelot { 3913169241fSVivien Didelot int port = dsa_towards_port(ds, info->sw_index, info->port); 392fac6abd5SVladimir Oltean struct dsa_port *dp = dsa_to_port(ds, port); 393685fb6a4SVivien Didelot 3941b6dd556SArkadi Sharshevsky if (!ds->ops->port_fdb_add) 395685fb6a4SVivien Didelot return -EOPNOTSUPP; 396685fb6a4SVivien Didelot 397fac6abd5SVladimir Oltean return dsa_port_do_fdb_add(dp, info->addr, info->vid); 398685fb6a4SVivien Didelot } 399685fb6a4SVivien Didelot 400685fb6a4SVivien Didelot static int dsa_switch_fdb_del(struct dsa_switch *ds, 401685fb6a4SVivien Didelot struct dsa_notifier_fdb_info *info) 402685fb6a4SVivien Didelot { 4033169241fSVivien Didelot int port = dsa_towards_port(ds, info->sw_index, info->port); 404fac6abd5SVladimir Oltean struct dsa_port *dp = dsa_to_port(ds, port); 405685fb6a4SVivien Didelot 406685fb6a4SVivien Didelot if (!ds->ops->port_fdb_del) 407685fb6a4SVivien Didelot return -EOPNOTSUPP; 408685fb6a4SVivien Didelot 409fac6abd5SVladimir Oltean return dsa_port_do_fdb_del(dp, info->addr, info->vid); 410685fb6a4SVivien Didelot } 411685fb6a4SVivien Didelot 41218596f50SGeorge McCollister static int dsa_switch_hsr_join(struct dsa_switch *ds, 41318596f50SGeorge McCollister struct dsa_notifier_hsr_info *info) 41418596f50SGeorge McCollister { 41518596f50SGeorge McCollister if (ds->index == info->sw_index && ds->ops->port_hsr_join) 41618596f50SGeorge McCollister return ds->ops->port_hsr_join(ds, info->port, info->hsr); 41718596f50SGeorge McCollister 41818596f50SGeorge McCollister return -EOPNOTSUPP; 41918596f50SGeorge McCollister } 42018596f50SGeorge McCollister 42118596f50SGeorge McCollister static int dsa_switch_hsr_leave(struct dsa_switch *ds, 42218596f50SGeorge McCollister struct dsa_notifier_hsr_info *info) 42318596f50SGeorge McCollister { 42418596f50SGeorge McCollister if (ds->index == info->sw_index && ds->ops->port_hsr_leave) 42518596f50SGeorge McCollister return ds->ops->port_hsr_leave(ds, info->port, info->hsr); 42618596f50SGeorge McCollister 42718596f50SGeorge McCollister return -EOPNOTSUPP; 42818596f50SGeorge McCollister } 42918596f50SGeorge McCollister 430058102a6STobias Waldekranz static int dsa_switch_lag_change(struct dsa_switch *ds, 431058102a6STobias Waldekranz struct dsa_notifier_lag_info *info) 432058102a6STobias Waldekranz { 433058102a6STobias Waldekranz if (ds->index == info->sw_index && ds->ops->port_lag_change) 434058102a6STobias Waldekranz return ds->ops->port_lag_change(ds, info->port); 435058102a6STobias Waldekranz 436058102a6STobias Waldekranz if (ds->index != info->sw_index && ds->ops->crosschip_lag_change) 437058102a6STobias Waldekranz return ds->ops->crosschip_lag_change(ds, info->sw_index, 438058102a6STobias Waldekranz info->port); 439058102a6STobias Waldekranz 440058102a6STobias Waldekranz return 0; 441058102a6STobias Waldekranz } 442058102a6STobias Waldekranz 443058102a6STobias Waldekranz static int dsa_switch_lag_join(struct dsa_switch *ds, 444058102a6STobias Waldekranz struct dsa_notifier_lag_info *info) 445058102a6STobias Waldekranz { 446058102a6STobias Waldekranz if (ds->index == info->sw_index && ds->ops->port_lag_join) 447058102a6STobias Waldekranz return ds->ops->port_lag_join(ds, info->port, info->lag, 448058102a6STobias Waldekranz info->info); 449058102a6STobias Waldekranz 450058102a6STobias Waldekranz if (ds->index != info->sw_index && ds->ops->crosschip_lag_join) 451058102a6STobias Waldekranz return ds->ops->crosschip_lag_join(ds, info->sw_index, 452058102a6STobias Waldekranz info->port, info->lag, 453058102a6STobias Waldekranz info->info); 454058102a6STobias Waldekranz 455b71d0987SVladimir Oltean return -EOPNOTSUPP; 456058102a6STobias Waldekranz } 457058102a6STobias Waldekranz 458058102a6STobias Waldekranz static int dsa_switch_lag_leave(struct dsa_switch *ds, 459058102a6STobias Waldekranz struct dsa_notifier_lag_info *info) 460058102a6STobias Waldekranz { 461058102a6STobias Waldekranz if (ds->index == info->sw_index && ds->ops->port_lag_leave) 462058102a6STobias Waldekranz return ds->ops->port_lag_leave(ds, info->port, info->lag); 463058102a6STobias Waldekranz 464058102a6STobias Waldekranz if (ds->index != info->sw_index && ds->ops->crosschip_lag_leave) 465058102a6STobias Waldekranz return ds->ops->crosschip_lag_leave(ds, info->sw_index, 466058102a6STobias Waldekranz info->port, info->lag); 467058102a6STobias Waldekranz 468b71d0987SVladimir Oltean return -EOPNOTSUPP; 469058102a6STobias Waldekranz } 470058102a6STobias Waldekranz 471ffb68fc5SVladimir Oltean static int dsa_switch_mdb_add(struct dsa_switch *ds, 472e65d45ccSVivien Didelot struct dsa_notifier_mdb_info *info) 473e6db98dbSVivien Didelot { 474abd49535SVladimir Oltean int port = dsa_towards_port(ds, info->sw_index, info->port); 475fac6abd5SVladimir Oltean struct dsa_port *dp = dsa_to_port(ds, port); 476e6db98dbSVivien Didelot 477a52b2da7SVladimir Oltean if (!ds->ops->port_mdb_add) 478e6db98dbSVivien Didelot return -EOPNOTSUPP; 479e6db98dbSVivien Didelot 480fac6abd5SVladimir Oltean return dsa_port_do_mdb_add(dp, info->mdb); 481e6db98dbSVivien Didelot } 4828ae5bcdcSVivien Didelot 4838ae5bcdcSVivien Didelot static int dsa_switch_mdb_del(struct dsa_switch *ds, 4848ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info *info) 4858ae5bcdcSVivien Didelot { 486161ca59dSVladimir Oltean int port = dsa_towards_port(ds, info->sw_index, info->port); 487fac6abd5SVladimir Oltean struct dsa_port *dp = dsa_to_port(ds, port); 488161ca59dSVladimir Oltean 4898ae5bcdcSVivien Didelot if (!ds->ops->port_mdb_del) 4908ae5bcdcSVivien Didelot return -EOPNOTSUPP; 4918ae5bcdcSVivien Didelot 492fac6abd5SVladimir Oltean return dsa_port_do_mdb_del(dp, info->mdb); 4938ae5bcdcSVivien Didelot } 4948ae5bcdcSVivien Didelot 495b8e997c4SVladimir Oltean static int dsa_switch_host_mdb_add(struct dsa_switch *ds, 496b8e997c4SVladimir Oltean struct dsa_notifier_mdb_info *info) 497b8e997c4SVladimir Oltean { 498fac6abd5SVladimir Oltean struct dsa_port *dp; 499b8e997c4SVladimir Oltean int err = 0; 500b8e997c4SVladimir Oltean 501b8e997c4SVladimir Oltean if (!ds->ops->port_mdb_add) 502b8e997c4SVladimir Oltean return -EOPNOTSUPP; 503b8e997c4SVladimir Oltean 504fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 505fac6abd5SVladimir Oltean if (dsa_port_host_address_match(dp, info->sw_index, 506b8e997c4SVladimir Oltean info->port)) { 507fac6abd5SVladimir Oltean err = dsa_port_do_mdb_add(dp, info->mdb); 508b8e997c4SVladimir Oltean if (err) 509b8e997c4SVladimir Oltean break; 510b8e997c4SVladimir Oltean } 511b8e997c4SVladimir Oltean } 512b8e997c4SVladimir Oltean 513b8e997c4SVladimir Oltean return err; 514b8e997c4SVladimir Oltean } 515b8e997c4SVladimir Oltean 516b8e997c4SVladimir Oltean static int dsa_switch_host_mdb_del(struct dsa_switch *ds, 517b8e997c4SVladimir Oltean struct dsa_notifier_mdb_info *info) 518b8e997c4SVladimir Oltean { 519fac6abd5SVladimir Oltean struct dsa_port *dp; 520161ca59dSVladimir Oltean int err = 0; 521161ca59dSVladimir Oltean 522b8e997c4SVladimir Oltean if (!ds->ops->port_mdb_del) 523b8e997c4SVladimir Oltean return -EOPNOTSUPP; 524b8e997c4SVladimir Oltean 525fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 526fac6abd5SVladimir Oltean if (dsa_port_host_address_match(dp, info->sw_index, 527161ca59dSVladimir Oltean info->port)) { 528fac6abd5SVladimir Oltean err = dsa_port_do_mdb_del(dp, info->mdb); 529161ca59dSVladimir Oltean if (err) 530161ca59dSVladimir Oltean break; 531161ca59dSVladimir Oltean } 532161ca59dSVladimir Oltean } 533b8e997c4SVladimir Oltean 534161ca59dSVladimir Oltean return err; 535b8e997c4SVladimir Oltean } 536b8e997c4SVladimir Oltean 537fac6abd5SVladimir Oltean static bool dsa_port_vlan_match(struct dsa_port *dp, 538e65d45ccSVivien Didelot struct dsa_notifier_vlan_info *info) 539e65d45ccSVivien Didelot { 540fac6abd5SVladimir Oltean if (dp->ds->index == info->sw_index && dp->index == info->port) 541e65d45ccSVivien Didelot return true; 542e65d45ccSVivien Didelot 543fac6abd5SVladimir Oltean if (dsa_port_is_dsa(dp)) 544e65d45ccSVivien Didelot return true; 545e65d45ccSVivien Didelot 546e65d45ccSVivien Didelot return false; 547e65d45ccSVivien Didelot } 548e65d45ccSVivien Didelot 549ffb68fc5SVladimir Oltean static int dsa_switch_vlan_add(struct dsa_switch *ds, 550e65d45ccSVivien Didelot struct dsa_notifier_vlan_info *info) 5519c428c59SVivien Didelot { 552fac6abd5SVladimir Oltean struct dsa_port *dp; 553fac6abd5SVladimir Oltean int err; 5549c428c59SVivien Didelot 5551958d581SVladimir Oltean if (!ds->ops->port_vlan_add) 5569c428c59SVivien Didelot return -EOPNOTSUPP; 5579c428c59SVivien Didelot 558fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 559fac6abd5SVladimir Oltean if (dsa_port_vlan_match(dp, info)) { 560fac6abd5SVladimir Oltean err = ds->ops->port_vlan_add(ds, dp->index, info->vlan, 56131046a5fSVladimir Oltean info->extack); 5629c428c59SVivien Didelot if (err) 5639c428c59SVivien Didelot return err; 5649c428c59SVivien Didelot } 565e65d45ccSVivien Didelot } 5669c428c59SVivien Didelot 567d0c627b8SVivien Didelot return 0; 568d0c627b8SVivien Didelot } 569d0c627b8SVivien Didelot 570d0c627b8SVivien Didelot static int dsa_switch_vlan_del(struct dsa_switch *ds, 571d0c627b8SVivien Didelot struct dsa_notifier_vlan_info *info) 572d0c627b8SVivien Didelot { 573d0c627b8SVivien Didelot if (!ds->ops->port_vlan_del) 574d0c627b8SVivien Didelot return -EOPNOTSUPP; 575d0c627b8SVivien Didelot 5761ca4aa9cSVivien Didelot if (ds->index == info->sw_index) 577e65d45ccSVivien Didelot return ds->ops->port_vlan_del(ds, info->port, info->vlan); 5781ca4aa9cSVivien Didelot 5797e1741b4SVivien Didelot /* Do not deprogram the DSA links as they may be used as conduit 5807e1741b4SVivien Didelot * for other VLAN members in the fabric. 5817e1741b4SVivien Didelot */ 5821ca4aa9cSVivien Didelot return 0; 583d0c627b8SVivien Didelot } 584d0c627b8SVivien Didelot 58553da0ebaSVladimir Oltean static int dsa_switch_change_tag_proto(struct dsa_switch *ds, 58653da0ebaSVladimir Oltean struct dsa_notifier_tag_proto_info *info) 58753da0ebaSVladimir Oltean { 58853da0ebaSVladimir Oltean const struct dsa_device_ops *tag_ops = info->tag_ops; 589d0004a02SVladimir Oltean struct dsa_port *dp, *cpu_dp; 590d0004a02SVladimir Oltean int err; 59153da0ebaSVladimir Oltean 59253da0ebaSVladimir Oltean if (!ds->ops->change_tag_protocol) 59353da0ebaSVladimir Oltean return -EOPNOTSUPP; 59453da0ebaSVladimir Oltean 59553da0ebaSVladimir Oltean ASSERT_RTNL(); 59653da0ebaSVladimir Oltean 597d0004a02SVladimir Oltean dsa_switch_for_each_cpu_port(cpu_dp, ds) { 598d0004a02SVladimir Oltean err = ds->ops->change_tag_protocol(ds, cpu_dp->index, 599d0004a02SVladimir Oltean tag_ops->proto); 60053da0ebaSVladimir Oltean if (err) 60153da0ebaSVladimir Oltean return err; 60253da0ebaSVladimir Oltean 603d0004a02SVladimir Oltean dsa_port_set_tag_protocol(cpu_dp, tag_ops); 60453da0ebaSVladimir Oltean } 60553da0ebaSVladimir Oltean 60653da0ebaSVladimir Oltean /* Now that changing the tag protocol can no longer fail, let's update 60753da0ebaSVladimir Oltean * the remaining bits which are "duplicated for faster access", and the 60853da0ebaSVladimir Oltean * bits that depend on the tagger, such as the MTU. 60953da0ebaSVladimir Oltean */ 610d0004a02SVladimir Oltean dsa_switch_for_each_user_port(dp, ds) { 611d0004a02SVladimir Oltean struct net_device *slave = dp->slave; 61253da0ebaSVladimir Oltean 61353da0ebaSVladimir Oltean dsa_slave_setup_tagger(slave); 61453da0ebaSVladimir Oltean 61553da0ebaSVladimir Oltean /* rtnl_mutex is held in dsa_tree_change_tag_proto */ 61653da0ebaSVladimir Oltean dsa_slave_change_mtu(slave, slave->mtu); 61753da0ebaSVladimir Oltean } 61853da0ebaSVladimir Oltean 61953da0ebaSVladimir Oltean return 0; 62053da0ebaSVladimir Oltean } 62153da0ebaSVladimir Oltean 622c595c433SHoratiu Vultur static int dsa_switch_mrp_add(struct dsa_switch *ds, 623c595c433SHoratiu Vultur struct dsa_notifier_mrp_info *info) 624c595c433SHoratiu Vultur { 625c595c433SHoratiu Vultur if (!ds->ops->port_mrp_add) 626c595c433SHoratiu Vultur return -EOPNOTSUPP; 627c595c433SHoratiu Vultur 628f9bcdc36SVladimir Oltean if (ds->index == info->sw_index) 629f9bcdc36SVladimir Oltean return ds->ops->port_mrp_add(ds, info->port, info->mrp); 630c595c433SHoratiu Vultur 631f9bcdc36SVladimir Oltean return 0; 632c595c433SHoratiu Vultur } 633c595c433SHoratiu Vultur 634c595c433SHoratiu Vultur static int dsa_switch_mrp_del(struct dsa_switch *ds, 635c595c433SHoratiu Vultur struct dsa_notifier_mrp_info *info) 636c595c433SHoratiu Vultur { 637c595c433SHoratiu Vultur if (!ds->ops->port_mrp_del) 638c595c433SHoratiu Vultur return -EOPNOTSUPP; 639c595c433SHoratiu Vultur 640c595c433SHoratiu Vultur if (ds->index == info->sw_index) 641c595c433SHoratiu Vultur return ds->ops->port_mrp_del(ds, info->port, info->mrp); 642c595c433SHoratiu Vultur 643c595c433SHoratiu Vultur return 0; 644c595c433SHoratiu Vultur } 645c595c433SHoratiu Vultur 646c595c433SHoratiu Vultur static int 647c595c433SHoratiu Vultur dsa_switch_mrp_add_ring_role(struct dsa_switch *ds, 648c595c433SHoratiu Vultur struct dsa_notifier_mrp_ring_role_info *info) 649c595c433SHoratiu Vultur { 650c595c433SHoratiu Vultur if (!ds->ops->port_mrp_add) 651c595c433SHoratiu Vultur return -EOPNOTSUPP; 652c595c433SHoratiu Vultur 653f9bcdc36SVladimir Oltean if (ds->index == info->sw_index) 654f9bcdc36SVladimir Oltean return ds->ops->port_mrp_add_ring_role(ds, info->port, 655c595c433SHoratiu Vultur info->mrp); 656c595c433SHoratiu Vultur 657f9bcdc36SVladimir Oltean return 0; 658c595c433SHoratiu Vultur } 659c595c433SHoratiu Vultur 660c595c433SHoratiu Vultur static int 661c595c433SHoratiu Vultur dsa_switch_mrp_del_ring_role(struct dsa_switch *ds, 662c595c433SHoratiu Vultur struct dsa_notifier_mrp_ring_role_info *info) 663c595c433SHoratiu Vultur { 664c595c433SHoratiu Vultur if (!ds->ops->port_mrp_del) 665c595c433SHoratiu Vultur return -EOPNOTSUPP; 666c595c433SHoratiu Vultur 667c595c433SHoratiu Vultur if (ds->index == info->sw_index) 668c595c433SHoratiu Vultur return ds->ops->port_mrp_del_ring_role(ds, info->port, 669c595c433SHoratiu Vultur info->mrp); 670c595c433SHoratiu Vultur 671c595c433SHoratiu Vultur return 0; 672c595c433SHoratiu Vultur } 673c595c433SHoratiu Vultur 674f515f192SVivien Didelot static int dsa_switch_event(struct notifier_block *nb, 675f515f192SVivien Didelot unsigned long event, void *info) 676f515f192SVivien Didelot { 677f515f192SVivien Didelot struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb); 678f515f192SVivien Didelot int err; 679f515f192SVivien Didelot 680f515f192SVivien Didelot switch (event) { 6811faabf74SVivien Didelot case DSA_NOTIFIER_AGEING_TIME: 6821faabf74SVivien Didelot err = dsa_switch_ageing_time(ds, info); 6831faabf74SVivien Didelot break; 68404d3a4c6SVivien Didelot case DSA_NOTIFIER_BRIDGE_JOIN: 68504d3a4c6SVivien Didelot err = dsa_switch_bridge_join(ds, info); 68604d3a4c6SVivien Didelot break; 68704d3a4c6SVivien Didelot case DSA_NOTIFIER_BRIDGE_LEAVE: 68804d3a4c6SVivien Didelot err = dsa_switch_bridge_leave(ds, info); 68904d3a4c6SVivien Didelot break; 690685fb6a4SVivien Didelot case DSA_NOTIFIER_FDB_ADD: 691685fb6a4SVivien Didelot err = dsa_switch_fdb_add(ds, info); 692685fb6a4SVivien Didelot break; 693685fb6a4SVivien Didelot case DSA_NOTIFIER_FDB_DEL: 694685fb6a4SVivien Didelot err = dsa_switch_fdb_del(ds, info); 695685fb6a4SVivien Didelot break; 6963dc80afcSVladimir Oltean case DSA_NOTIFIER_HOST_FDB_ADD: 6973dc80afcSVladimir Oltean err = dsa_switch_host_fdb_add(ds, info); 6983dc80afcSVladimir Oltean break; 6993dc80afcSVladimir Oltean case DSA_NOTIFIER_HOST_FDB_DEL: 7003dc80afcSVladimir Oltean err = dsa_switch_host_fdb_del(ds, info); 7013dc80afcSVladimir Oltean break; 70218596f50SGeorge McCollister case DSA_NOTIFIER_HSR_JOIN: 70318596f50SGeorge McCollister err = dsa_switch_hsr_join(ds, info); 70418596f50SGeorge McCollister break; 70518596f50SGeorge McCollister case DSA_NOTIFIER_HSR_LEAVE: 70618596f50SGeorge McCollister err = dsa_switch_hsr_leave(ds, info); 70718596f50SGeorge McCollister break; 708058102a6STobias Waldekranz case DSA_NOTIFIER_LAG_CHANGE: 709058102a6STobias Waldekranz err = dsa_switch_lag_change(ds, info); 710058102a6STobias Waldekranz break; 711058102a6STobias Waldekranz case DSA_NOTIFIER_LAG_JOIN: 712058102a6STobias Waldekranz err = dsa_switch_lag_join(ds, info); 713058102a6STobias Waldekranz break; 714058102a6STobias Waldekranz case DSA_NOTIFIER_LAG_LEAVE: 715058102a6STobias Waldekranz err = dsa_switch_lag_leave(ds, info); 716058102a6STobias Waldekranz break; 7178ae5bcdcSVivien Didelot case DSA_NOTIFIER_MDB_ADD: 7188ae5bcdcSVivien Didelot err = dsa_switch_mdb_add(ds, info); 7198ae5bcdcSVivien Didelot break; 7208ae5bcdcSVivien Didelot case DSA_NOTIFIER_MDB_DEL: 7218ae5bcdcSVivien Didelot err = dsa_switch_mdb_del(ds, info); 7228ae5bcdcSVivien Didelot break; 723b8e997c4SVladimir Oltean case DSA_NOTIFIER_HOST_MDB_ADD: 724b8e997c4SVladimir Oltean err = dsa_switch_host_mdb_add(ds, info); 725b8e997c4SVladimir Oltean break; 726b8e997c4SVladimir Oltean case DSA_NOTIFIER_HOST_MDB_DEL: 727b8e997c4SVladimir Oltean err = dsa_switch_host_mdb_del(ds, info); 728b8e997c4SVladimir Oltean break; 729d0c627b8SVivien Didelot case DSA_NOTIFIER_VLAN_ADD: 730d0c627b8SVivien Didelot err = dsa_switch_vlan_add(ds, info); 731d0c627b8SVivien Didelot break; 732d0c627b8SVivien Didelot case DSA_NOTIFIER_VLAN_DEL: 733d0c627b8SVivien Didelot err = dsa_switch_vlan_del(ds, info); 734d0c627b8SVivien Didelot break; 735bfcb8132SVladimir Oltean case DSA_NOTIFIER_MTU: 736bfcb8132SVladimir Oltean err = dsa_switch_mtu(ds, info); 737bfcb8132SVladimir Oltean break; 73853da0ebaSVladimir Oltean case DSA_NOTIFIER_TAG_PROTO: 73953da0ebaSVladimir Oltean err = dsa_switch_change_tag_proto(ds, info); 74053da0ebaSVladimir Oltean break; 741c595c433SHoratiu Vultur case DSA_NOTIFIER_MRP_ADD: 742c595c433SHoratiu Vultur err = dsa_switch_mrp_add(ds, info); 743c595c433SHoratiu Vultur break; 744c595c433SHoratiu Vultur case DSA_NOTIFIER_MRP_DEL: 745c595c433SHoratiu Vultur err = dsa_switch_mrp_del(ds, info); 746c595c433SHoratiu Vultur break; 747c595c433SHoratiu Vultur case DSA_NOTIFIER_MRP_ADD_RING_ROLE: 748c595c433SHoratiu Vultur err = dsa_switch_mrp_add_ring_role(ds, info); 749c595c433SHoratiu Vultur break; 750c595c433SHoratiu Vultur case DSA_NOTIFIER_MRP_DEL_RING_ROLE: 751c595c433SHoratiu Vultur err = dsa_switch_mrp_del_ring_role(ds, info); 752c595c433SHoratiu Vultur break; 753c64b9c05SVladimir Oltean case DSA_NOTIFIER_TAG_8021Q_VLAN_ADD: 754c64b9c05SVladimir Oltean err = dsa_switch_tag_8021q_vlan_add(ds, info); 755c64b9c05SVladimir Oltean break; 756c64b9c05SVladimir Oltean case DSA_NOTIFIER_TAG_8021Q_VLAN_DEL: 757c64b9c05SVladimir Oltean err = dsa_switch_tag_8021q_vlan_del(ds, info); 758c64b9c05SVladimir Oltean break; 759f515f192SVivien Didelot default: 760f515f192SVivien Didelot err = -EOPNOTSUPP; 761f515f192SVivien Didelot break; 762f515f192SVivien Didelot } 763f515f192SVivien Didelot 764f515f192SVivien Didelot if (err) 765f515f192SVivien Didelot dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n", 766f515f192SVivien Didelot event, err); 767f515f192SVivien Didelot 768f515f192SVivien Didelot return notifier_from_errno(err); 769f515f192SVivien Didelot } 770f515f192SVivien Didelot 771f515f192SVivien Didelot int dsa_switch_register_notifier(struct dsa_switch *ds) 772f515f192SVivien Didelot { 773f515f192SVivien Didelot ds->nb.notifier_call = dsa_switch_event; 774f515f192SVivien Didelot 775f515f192SVivien Didelot return raw_notifier_chain_register(&ds->dst->nh, &ds->nb); 776f515f192SVivien Didelot } 777f515f192SVivien Didelot 778f515f192SVivien Didelot void dsa_switch_unregister_notifier(struct dsa_switch *ds) 779f515f192SVivien Didelot { 780f515f192SVivien Didelot int err; 781f515f192SVivien Didelot 782f515f192SVivien Didelot err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb); 783f515f192SVivien Didelot if (err) 784f515f192SVivien Didelot dev_err(ds->dev, "failed to unregister notifier (%d)\n", err); 785f515f192SVivien Didelot } 786