12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2f515f192SVivien Didelot /* 3f515f192SVivien Didelot * Handling of a single switch chip, part of a switch fabric 4f515f192SVivien Didelot * 54333d619SVivien Didelot * Copyright (c) 2017 Savoir-faire Linux Inc. 64333d619SVivien Didelot * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 7f515f192SVivien Didelot */ 8f515f192SVivien Didelot 9d371b7c9SVladimir Oltean #include <linux/if_bridge.h> 10f515f192SVivien Didelot #include <linux/netdevice.h> 11f515f192SVivien Didelot #include <linux/notifier.h> 12061f6a50SFlorian Fainelli #include <linux/if_vlan.h> 131faabf74SVivien Didelot #include <net/switchdev.h> 14ea5dd34bSVivien Didelot 15ea5dd34bSVivien Didelot #include "dsa_priv.h" 16f515f192SVivien Didelot 171faabf74SVivien Didelot static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds, 181faabf74SVivien Didelot unsigned int ageing_time) 191faabf74SVivien Didelot { 201faabf74SVivien Didelot int i; 211faabf74SVivien Didelot 221faabf74SVivien Didelot for (i = 0; i < ds->num_ports; ++i) { 2368bb8ea8SVivien Didelot struct dsa_port *dp = dsa_to_port(ds, i); 241faabf74SVivien Didelot 251faabf74SVivien Didelot if (dp->ageing_time && dp->ageing_time < ageing_time) 261faabf74SVivien Didelot ageing_time = dp->ageing_time; 271faabf74SVivien Didelot } 281faabf74SVivien Didelot 291faabf74SVivien Didelot return ageing_time; 301faabf74SVivien Didelot } 311faabf74SVivien Didelot 321faabf74SVivien Didelot static int dsa_switch_ageing_time(struct dsa_switch *ds, 331faabf74SVivien Didelot struct dsa_notifier_ageing_time_info *info) 341faabf74SVivien Didelot { 351faabf74SVivien Didelot unsigned int ageing_time = info->ageing_time; 361faabf74SVivien Didelot 371faabf74SVivien Didelot if (ds->ageing_time_min && ageing_time < ds->ageing_time_min) 381faabf74SVivien Didelot return -ERANGE; 3977b61365SVladimir Oltean 401faabf74SVivien Didelot if (ds->ageing_time_max && ageing_time > ds->ageing_time_max) 411faabf74SVivien Didelot return -ERANGE; 421faabf74SVivien Didelot 431faabf74SVivien Didelot /* Program the fastest ageing time in case of multiple bridges */ 441faabf74SVivien Didelot ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time); 451faabf74SVivien Didelot 461faabf74SVivien Didelot if (ds->ops->set_ageing_time) 471faabf74SVivien Didelot return ds->ops->set_ageing_time(ds, ageing_time); 481faabf74SVivien Didelot 491faabf74SVivien Didelot return 0; 501faabf74SVivien Didelot } 511faabf74SVivien Didelot 52bfcb8132SVladimir Oltean static bool dsa_switch_mtu_match(struct dsa_switch *ds, int port, 53bfcb8132SVladimir Oltean struct dsa_notifier_mtu_info *info) 54bfcb8132SVladimir Oltean { 55bfcb8132SVladimir Oltean if (ds->index == info->sw_index) 56bfcb8132SVladimir Oltean return (port == info->port) || dsa_is_dsa_port(ds, port); 57bfcb8132SVladimir Oltean 58bfcb8132SVladimir Oltean if (!info->propagate_upstream) 59bfcb8132SVladimir Oltean return false; 60bfcb8132SVladimir Oltean 61bfcb8132SVladimir Oltean if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port)) 62bfcb8132SVladimir Oltean return true; 63bfcb8132SVladimir Oltean 64bfcb8132SVladimir Oltean return false; 65bfcb8132SVladimir Oltean } 66bfcb8132SVladimir Oltean 67bfcb8132SVladimir Oltean static int dsa_switch_mtu(struct dsa_switch *ds, 68bfcb8132SVladimir Oltean struct dsa_notifier_mtu_info *info) 69bfcb8132SVladimir Oltean { 70bfcb8132SVladimir Oltean int port, ret; 71bfcb8132SVladimir Oltean 72bfcb8132SVladimir Oltean if (!ds->ops->port_change_mtu) 73bfcb8132SVladimir Oltean return -EOPNOTSUPP; 74bfcb8132SVladimir Oltean 75bfcb8132SVladimir Oltean for (port = 0; port < ds->num_ports; port++) { 76bfcb8132SVladimir Oltean if (dsa_switch_mtu_match(ds, port, info)) { 77bfcb8132SVladimir Oltean ret = ds->ops->port_change_mtu(ds, port, info->mtu); 78bfcb8132SVladimir Oltean if (ret) 79bfcb8132SVladimir Oltean return ret; 80bfcb8132SVladimir Oltean } 81bfcb8132SVladimir Oltean } 82bfcb8132SVladimir Oltean 83bfcb8132SVladimir Oltean return 0; 84bfcb8132SVladimir Oltean } 85bfcb8132SVladimir Oltean 8604d3a4c6SVivien Didelot static int dsa_switch_bridge_join(struct dsa_switch *ds, 8704d3a4c6SVivien Didelot struct dsa_notifier_bridge_info *info) 8804d3a4c6SVivien Didelot { 89f66a6a69SVladimir Oltean struct dsa_switch_tree *dst = ds->dst; 90f66a6a69SVladimir Oltean 91f66a6a69SVladimir Oltean if (dst->index == info->tree_index && ds->index == info->sw_index && 92f66a6a69SVladimir Oltean ds->ops->port_bridge_join) 9304d3a4c6SVivien Didelot return ds->ops->port_bridge_join(ds, info->port, info->br); 9404d3a4c6SVivien Didelot 95f66a6a69SVladimir Oltean if ((dst->index != info->tree_index || ds->index != info->sw_index) && 96f66a6a69SVladimir Oltean ds->ops->crosschip_bridge_join) 97f66a6a69SVladimir Oltean return ds->ops->crosschip_bridge_join(ds, info->tree_index, 98f66a6a69SVladimir Oltean info->sw_index, 9940ef2c93SVivien Didelot info->port, info->br); 10004d3a4c6SVivien Didelot 10104d3a4c6SVivien Didelot return 0; 10204d3a4c6SVivien Didelot } 10304d3a4c6SVivien Didelot 10404d3a4c6SVivien Didelot static int dsa_switch_bridge_leave(struct dsa_switch *ds, 10504d3a4c6SVivien Didelot struct dsa_notifier_bridge_info *info) 10604d3a4c6SVivien Didelot { 107d371b7c9SVladimir Oltean bool unset_vlan_filtering = br_vlan_enabled(info->br); 108f66a6a69SVladimir Oltean struct dsa_switch_tree *dst = ds->dst; 109*89153ed6SVladimir Oltean struct netlink_ext_ack extack = {0}; 110d371b7c9SVladimir Oltean int err, i; 111d371b7c9SVladimir Oltean 112f66a6a69SVladimir Oltean if (dst->index == info->tree_index && ds->index == info->sw_index && 113f66a6a69SVladimir Oltean ds->ops->port_bridge_join) 11404d3a4c6SVivien Didelot ds->ops->port_bridge_leave(ds, info->port, info->br); 11504d3a4c6SVivien Didelot 116f66a6a69SVladimir Oltean if ((dst->index != info->tree_index || ds->index != info->sw_index) && 117f66a6a69SVladimir Oltean ds->ops->crosschip_bridge_join) 118f66a6a69SVladimir Oltean ds->ops->crosschip_bridge_leave(ds, info->tree_index, 119f66a6a69SVladimir Oltean info->sw_index, info->port, 12040ef2c93SVivien Didelot info->br); 12104d3a4c6SVivien Didelot 122d371b7c9SVladimir Oltean /* If the bridge was vlan_filtering, the bridge core doesn't trigger an 123d371b7c9SVladimir Oltean * event for changing vlan_filtering setting upon slave ports leaving 124d371b7c9SVladimir Oltean * it. That is a good thing, because that lets us handle it and also 125d371b7c9SVladimir Oltean * handle the case where the switch's vlan_filtering setting is global 126d371b7c9SVladimir Oltean * (not per port). When that happens, the correct moment to trigger the 127d371b7c9SVladimir Oltean * vlan_filtering callback is only when the last port left this bridge. 128d371b7c9SVladimir Oltean */ 129d371b7c9SVladimir Oltean if (unset_vlan_filtering && ds->vlan_filtering_is_global) { 130d371b7c9SVladimir Oltean for (i = 0; i < ds->num_ports; i++) { 131d371b7c9SVladimir Oltean if (i == info->port) 132d371b7c9SVladimir Oltean continue; 133d371b7c9SVladimir Oltean if (dsa_to_port(ds, i)->bridge_dev == info->br) { 134d371b7c9SVladimir Oltean unset_vlan_filtering = false; 135d371b7c9SVladimir Oltean break; 136d371b7c9SVladimir Oltean } 137d371b7c9SVladimir Oltean } 138d371b7c9SVladimir Oltean } 139d371b7c9SVladimir Oltean if (unset_vlan_filtering) { 1402e554a7aSVladimir Oltean err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port), 141*89153ed6SVladimir Oltean false, &extack); 142*89153ed6SVladimir Oltean if (extack._msg) 143*89153ed6SVladimir Oltean dev_err(ds->dev, "port %d: %s\n", info->port, 144*89153ed6SVladimir Oltean extack._msg); 145d371b7c9SVladimir Oltean if (err && err != EOPNOTSUPP) 146d371b7c9SVladimir Oltean return err; 147d371b7c9SVladimir Oltean } 14804d3a4c6SVivien Didelot return 0; 14904d3a4c6SVivien Didelot } 15004d3a4c6SVivien Didelot 151685fb6a4SVivien Didelot static int dsa_switch_fdb_add(struct dsa_switch *ds, 152685fb6a4SVivien Didelot struct dsa_notifier_fdb_info *info) 153685fb6a4SVivien Didelot { 1543169241fSVivien Didelot int port = dsa_towards_port(ds, info->sw_index, info->port); 155685fb6a4SVivien Didelot 1561b6dd556SArkadi Sharshevsky if (!ds->ops->port_fdb_add) 157685fb6a4SVivien Didelot return -EOPNOTSUPP; 158685fb6a4SVivien Didelot 1593169241fSVivien Didelot return ds->ops->port_fdb_add(ds, port, info->addr, info->vid); 160685fb6a4SVivien Didelot } 161685fb6a4SVivien Didelot 162685fb6a4SVivien Didelot static int dsa_switch_fdb_del(struct dsa_switch *ds, 163685fb6a4SVivien Didelot struct dsa_notifier_fdb_info *info) 164685fb6a4SVivien Didelot { 1653169241fSVivien Didelot int port = dsa_towards_port(ds, info->sw_index, info->port); 166685fb6a4SVivien Didelot 167685fb6a4SVivien Didelot if (!ds->ops->port_fdb_del) 168685fb6a4SVivien Didelot return -EOPNOTSUPP; 169685fb6a4SVivien Didelot 1703169241fSVivien Didelot return ds->ops->port_fdb_del(ds, port, info->addr, info->vid); 171685fb6a4SVivien Didelot } 172685fb6a4SVivien Didelot 17318596f50SGeorge McCollister static int dsa_switch_hsr_join(struct dsa_switch *ds, 17418596f50SGeorge McCollister struct dsa_notifier_hsr_info *info) 17518596f50SGeorge McCollister { 17618596f50SGeorge McCollister if (ds->index == info->sw_index && ds->ops->port_hsr_join) 17718596f50SGeorge McCollister return ds->ops->port_hsr_join(ds, info->port, info->hsr); 17818596f50SGeorge McCollister 17918596f50SGeorge McCollister return -EOPNOTSUPP; 18018596f50SGeorge McCollister } 18118596f50SGeorge McCollister 18218596f50SGeorge McCollister static int dsa_switch_hsr_leave(struct dsa_switch *ds, 18318596f50SGeorge McCollister struct dsa_notifier_hsr_info *info) 18418596f50SGeorge McCollister { 18518596f50SGeorge McCollister if (ds->index == info->sw_index && ds->ops->port_hsr_leave) 18618596f50SGeorge McCollister return ds->ops->port_hsr_leave(ds, info->port, info->hsr); 18718596f50SGeorge McCollister 18818596f50SGeorge McCollister return -EOPNOTSUPP; 18918596f50SGeorge McCollister } 19018596f50SGeorge McCollister 191058102a6STobias Waldekranz static int dsa_switch_lag_change(struct dsa_switch *ds, 192058102a6STobias Waldekranz struct dsa_notifier_lag_info *info) 193058102a6STobias Waldekranz { 194058102a6STobias Waldekranz if (ds->index == info->sw_index && ds->ops->port_lag_change) 195058102a6STobias Waldekranz return ds->ops->port_lag_change(ds, info->port); 196058102a6STobias Waldekranz 197058102a6STobias Waldekranz if (ds->index != info->sw_index && ds->ops->crosschip_lag_change) 198058102a6STobias Waldekranz return ds->ops->crosschip_lag_change(ds, info->sw_index, 199058102a6STobias Waldekranz info->port); 200058102a6STobias Waldekranz 201058102a6STobias Waldekranz return 0; 202058102a6STobias Waldekranz } 203058102a6STobias Waldekranz 204058102a6STobias Waldekranz static int dsa_switch_lag_join(struct dsa_switch *ds, 205058102a6STobias Waldekranz struct dsa_notifier_lag_info *info) 206058102a6STobias Waldekranz { 207058102a6STobias Waldekranz if (ds->index == info->sw_index && ds->ops->port_lag_join) 208058102a6STobias Waldekranz return ds->ops->port_lag_join(ds, info->port, info->lag, 209058102a6STobias Waldekranz info->info); 210058102a6STobias Waldekranz 211058102a6STobias Waldekranz if (ds->index != info->sw_index && ds->ops->crosschip_lag_join) 212058102a6STobias Waldekranz return ds->ops->crosschip_lag_join(ds, info->sw_index, 213058102a6STobias Waldekranz info->port, info->lag, 214058102a6STobias Waldekranz info->info); 215058102a6STobias Waldekranz 216058102a6STobias Waldekranz return 0; 217058102a6STobias Waldekranz } 218058102a6STobias Waldekranz 219058102a6STobias Waldekranz static int dsa_switch_lag_leave(struct dsa_switch *ds, 220058102a6STobias Waldekranz struct dsa_notifier_lag_info *info) 221058102a6STobias Waldekranz { 222058102a6STobias Waldekranz if (ds->index == info->sw_index && ds->ops->port_lag_leave) 223058102a6STobias Waldekranz return ds->ops->port_lag_leave(ds, info->port, info->lag); 224058102a6STobias Waldekranz 225058102a6STobias Waldekranz if (ds->index != info->sw_index && ds->ops->crosschip_lag_leave) 226058102a6STobias Waldekranz return ds->ops->crosschip_lag_leave(ds, info->sw_index, 227058102a6STobias Waldekranz info->port, info->lag); 228058102a6STobias Waldekranz 229058102a6STobias Waldekranz return 0; 230058102a6STobias Waldekranz } 231058102a6STobias Waldekranz 232e65d45ccSVivien Didelot static bool dsa_switch_mdb_match(struct dsa_switch *ds, int port, 233e65d45ccSVivien Didelot struct dsa_notifier_mdb_info *info) 234e65d45ccSVivien Didelot { 235e65d45ccSVivien Didelot if (ds->index == info->sw_index && port == info->port) 236e65d45ccSVivien Didelot return true; 237e65d45ccSVivien Didelot 238e65d45ccSVivien Didelot if (dsa_is_dsa_port(ds, port)) 239e65d45ccSVivien Didelot return true; 240e65d45ccSVivien Didelot 241e65d45ccSVivien Didelot return false; 242e65d45ccSVivien Didelot } 243e65d45ccSVivien Didelot 244ffb68fc5SVladimir Oltean static int dsa_switch_mdb_add(struct dsa_switch *ds, 245e65d45ccSVivien Didelot struct dsa_notifier_mdb_info *info) 246e6db98dbSVivien Didelot { 247a52b2da7SVladimir Oltean int err = 0; 248a52b2da7SVladimir Oltean int port; 249e6db98dbSVivien Didelot 250a52b2da7SVladimir Oltean if (!ds->ops->port_mdb_add) 251e6db98dbSVivien Didelot return -EOPNOTSUPP; 252e6db98dbSVivien Didelot 253e65d45ccSVivien Didelot for (port = 0; port < ds->num_ports; port++) { 254e65d45ccSVivien Didelot if (dsa_switch_mdb_match(ds, port, info)) { 255a52b2da7SVladimir Oltean err = ds->ops->port_mdb_add(ds, port, info->mdb); 256e6db98dbSVivien Didelot if (err) 257a52b2da7SVladimir Oltean break; 258a52b2da7SVladimir Oltean } 259a52b2da7SVladimir Oltean } 260a52b2da7SVladimir Oltean 261e6db98dbSVivien Didelot return err; 262e6db98dbSVivien Didelot } 2638ae5bcdcSVivien Didelot 2648ae5bcdcSVivien Didelot static int dsa_switch_mdb_del(struct dsa_switch *ds, 2658ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info *info) 2668ae5bcdcSVivien Didelot { 2678ae5bcdcSVivien Didelot if (!ds->ops->port_mdb_del) 2688ae5bcdcSVivien Didelot return -EOPNOTSUPP; 2698ae5bcdcSVivien Didelot 270a1a6b7eaSVivien Didelot if (ds->index == info->sw_index) 271e65d45ccSVivien Didelot return ds->ops->port_mdb_del(ds, info->port, info->mdb); 272a1a6b7eaSVivien Didelot 273a1a6b7eaSVivien Didelot return 0; 2748ae5bcdcSVivien Didelot } 2758ae5bcdcSVivien Didelot 276e65d45ccSVivien Didelot static bool dsa_switch_vlan_match(struct dsa_switch *ds, int port, 277e65d45ccSVivien Didelot struct dsa_notifier_vlan_info *info) 278e65d45ccSVivien Didelot { 279e65d45ccSVivien Didelot if (ds->index == info->sw_index && port == info->port) 280e65d45ccSVivien Didelot return true; 281e65d45ccSVivien Didelot 2827e1741b4SVivien Didelot if (dsa_is_dsa_port(ds, port)) 283e65d45ccSVivien Didelot return true; 284e65d45ccSVivien Didelot 285e65d45ccSVivien Didelot return false; 286e65d45ccSVivien Didelot } 287e65d45ccSVivien Didelot 288ffb68fc5SVladimir Oltean static int dsa_switch_vlan_add(struct dsa_switch *ds, 289e65d45ccSVivien Didelot struct dsa_notifier_vlan_info *info) 2909c428c59SVivien Didelot { 2919c428c59SVivien Didelot int port, err; 2929c428c59SVivien Didelot 2931958d581SVladimir Oltean if (!ds->ops->port_vlan_add) 2949c428c59SVivien Didelot return -EOPNOTSUPP; 2959c428c59SVivien Didelot 296e65d45ccSVivien Didelot for (port = 0; port < ds->num_ports; port++) { 297e65d45ccSVivien Didelot if (dsa_switch_vlan_match(ds, port, info)) { 29831046a5fSVladimir Oltean err = ds->ops->port_vlan_add(ds, port, info->vlan, 29931046a5fSVladimir Oltean info->extack); 3009c428c59SVivien Didelot if (err) 3019c428c59SVivien Didelot return err; 3029c428c59SVivien Didelot } 303e65d45ccSVivien Didelot } 3049c428c59SVivien Didelot 305d0c627b8SVivien Didelot return 0; 306d0c627b8SVivien Didelot } 307d0c627b8SVivien Didelot 308d0c627b8SVivien Didelot static int dsa_switch_vlan_del(struct dsa_switch *ds, 309d0c627b8SVivien Didelot struct dsa_notifier_vlan_info *info) 310d0c627b8SVivien Didelot { 311d0c627b8SVivien Didelot if (!ds->ops->port_vlan_del) 312d0c627b8SVivien Didelot return -EOPNOTSUPP; 313d0c627b8SVivien Didelot 3141ca4aa9cSVivien Didelot if (ds->index == info->sw_index) 315e65d45ccSVivien Didelot return ds->ops->port_vlan_del(ds, info->port, info->vlan); 3161ca4aa9cSVivien Didelot 3177e1741b4SVivien Didelot /* Do not deprogram the DSA links as they may be used as conduit 3187e1741b4SVivien Didelot * for other VLAN members in the fabric. 3197e1741b4SVivien Didelot */ 3201ca4aa9cSVivien Didelot return 0; 321d0c627b8SVivien Didelot } 322d0c627b8SVivien Didelot 32353da0ebaSVladimir Oltean static bool dsa_switch_tag_proto_match(struct dsa_switch *ds, int port, 32453da0ebaSVladimir Oltean struct dsa_notifier_tag_proto_info *info) 32553da0ebaSVladimir Oltean { 32653da0ebaSVladimir Oltean if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) 32753da0ebaSVladimir Oltean return true; 32853da0ebaSVladimir Oltean 32953da0ebaSVladimir Oltean return false; 33053da0ebaSVladimir Oltean } 33153da0ebaSVladimir Oltean 33253da0ebaSVladimir Oltean static int dsa_switch_change_tag_proto(struct dsa_switch *ds, 33353da0ebaSVladimir Oltean struct dsa_notifier_tag_proto_info *info) 33453da0ebaSVladimir Oltean { 33553da0ebaSVladimir Oltean const struct dsa_device_ops *tag_ops = info->tag_ops; 33653da0ebaSVladimir Oltean int port, err; 33753da0ebaSVladimir Oltean 33853da0ebaSVladimir Oltean if (!ds->ops->change_tag_protocol) 33953da0ebaSVladimir Oltean return -EOPNOTSUPP; 34053da0ebaSVladimir Oltean 34153da0ebaSVladimir Oltean ASSERT_RTNL(); 34253da0ebaSVladimir Oltean 34353da0ebaSVladimir Oltean for (port = 0; port < ds->num_ports; port++) { 34453da0ebaSVladimir Oltean if (dsa_switch_tag_proto_match(ds, port, info)) { 34553da0ebaSVladimir Oltean err = ds->ops->change_tag_protocol(ds, port, 34653da0ebaSVladimir Oltean tag_ops->proto); 34753da0ebaSVladimir Oltean if (err) 34853da0ebaSVladimir Oltean return err; 34953da0ebaSVladimir Oltean 35053da0ebaSVladimir Oltean if (dsa_is_cpu_port(ds, port)) 35153da0ebaSVladimir Oltean dsa_port_set_tag_protocol(dsa_to_port(ds, port), 35253da0ebaSVladimir Oltean tag_ops); 35353da0ebaSVladimir Oltean } 35453da0ebaSVladimir Oltean } 35553da0ebaSVladimir Oltean 35653da0ebaSVladimir Oltean /* Now that changing the tag protocol can no longer fail, let's update 35753da0ebaSVladimir Oltean * the remaining bits which are "duplicated for faster access", and the 35853da0ebaSVladimir Oltean * bits that depend on the tagger, such as the MTU. 35953da0ebaSVladimir Oltean */ 36053da0ebaSVladimir Oltean for (port = 0; port < ds->num_ports; port++) { 36153da0ebaSVladimir Oltean if (dsa_is_user_port(ds, port)) { 36253da0ebaSVladimir Oltean struct net_device *slave; 36353da0ebaSVladimir Oltean 36453da0ebaSVladimir Oltean slave = dsa_to_port(ds, port)->slave; 36553da0ebaSVladimir Oltean dsa_slave_setup_tagger(slave); 36653da0ebaSVladimir Oltean 36753da0ebaSVladimir Oltean /* rtnl_mutex is held in dsa_tree_change_tag_proto */ 36853da0ebaSVladimir Oltean dsa_slave_change_mtu(slave, slave->mtu); 36953da0ebaSVladimir Oltean } 37053da0ebaSVladimir Oltean } 37153da0ebaSVladimir Oltean 37253da0ebaSVladimir Oltean return 0; 37353da0ebaSVladimir Oltean } 37453da0ebaSVladimir Oltean 375f515f192SVivien Didelot static int dsa_switch_event(struct notifier_block *nb, 376f515f192SVivien Didelot unsigned long event, void *info) 377f515f192SVivien Didelot { 378f515f192SVivien Didelot struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb); 379f515f192SVivien Didelot int err; 380f515f192SVivien Didelot 381f515f192SVivien Didelot switch (event) { 3821faabf74SVivien Didelot case DSA_NOTIFIER_AGEING_TIME: 3831faabf74SVivien Didelot err = dsa_switch_ageing_time(ds, info); 3841faabf74SVivien Didelot break; 38504d3a4c6SVivien Didelot case DSA_NOTIFIER_BRIDGE_JOIN: 38604d3a4c6SVivien Didelot err = dsa_switch_bridge_join(ds, info); 38704d3a4c6SVivien Didelot break; 38804d3a4c6SVivien Didelot case DSA_NOTIFIER_BRIDGE_LEAVE: 38904d3a4c6SVivien Didelot err = dsa_switch_bridge_leave(ds, info); 39004d3a4c6SVivien Didelot break; 391685fb6a4SVivien Didelot case DSA_NOTIFIER_FDB_ADD: 392685fb6a4SVivien Didelot err = dsa_switch_fdb_add(ds, info); 393685fb6a4SVivien Didelot break; 394685fb6a4SVivien Didelot case DSA_NOTIFIER_FDB_DEL: 395685fb6a4SVivien Didelot err = dsa_switch_fdb_del(ds, info); 396685fb6a4SVivien Didelot break; 39718596f50SGeorge McCollister case DSA_NOTIFIER_HSR_JOIN: 39818596f50SGeorge McCollister err = dsa_switch_hsr_join(ds, info); 39918596f50SGeorge McCollister break; 40018596f50SGeorge McCollister case DSA_NOTIFIER_HSR_LEAVE: 40118596f50SGeorge McCollister err = dsa_switch_hsr_leave(ds, info); 40218596f50SGeorge McCollister break; 403058102a6STobias Waldekranz case DSA_NOTIFIER_LAG_CHANGE: 404058102a6STobias Waldekranz err = dsa_switch_lag_change(ds, info); 405058102a6STobias Waldekranz break; 406058102a6STobias Waldekranz case DSA_NOTIFIER_LAG_JOIN: 407058102a6STobias Waldekranz err = dsa_switch_lag_join(ds, info); 408058102a6STobias Waldekranz break; 409058102a6STobias Waldekranz case DSA_NOTIFIER_LAG_LEAVE: 410058102a6STobias Waldekranz err = dsa_switch_lag_leave(ds, info); 411058102a6STobias Waldekranz break; 4128ae5bcdcSVivien Didelot case DSA_NOTIFIER_MDB_ADD: 4138ae5bcdcSVivien Didelot err = dsa_switch_mdb_add(ds, info); 4148ae5bcdcSVivien Didelot break; 4158ae5bcdcSVivien Didelot case DSA_NOTIFIER_MDB_DEL: 4168ae5bcdcSVivien Didelot err = dsa_switch_mdb_del(ds, info); 4178ae5bcdcSVivien Didelot break; 418d0c627b8SVivien Didelot case DSA_NOTIFIER_VLAN_ADD: 419d0c627b8SVivien Didelot err = dsa_switch_vlan_add(ds, info); 420d0c627b8SVivien Didelot break; 421d0c627b8SVivien Didelot case DSA_NOTIFIER_VLAN_DEL: 422d0c627b8SVivien Didelot err = dsa_switch_vlan_del(ds, info); 423d0c627b8SVivien Didelot break; 424bfcb8132SVladimir Oltean case DSA_NOTIFIER_MTU: 425bfcb8132SVladimir Oltean err = dsa_switch_mtu(ds, info); 426bfcb8132SVladimir Oltean break; 42753da0ebaSVladimir Oltean case DSA_NOTIFIER_TAG_PROTO: 42853da0ebaSVladimir Oltean err = dsa_switch_change_tag_proto(ds, info); 42953da0ebaSVladimir Oltean break; 430f515f192SVivien Didelot default: 431f515f192SVivien Didelot err = -EOPNOTSUPP; 432f515f192SVivien Didelot break; 433f515f192SVivien Didelot } 434f515f192SVivien Didelot 435f515f192SVivien Didelot if (err) 436f515f192SVivien Didelot dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n", 437f515f192SVivien Didelot event, err); 438f515f192SVivien Didelot 439f515f192SVivien Didelot return notifier_from_errno(err); 440f515f192SVivien Didelot } 441f515f192SVivien Didelot 442f515f192SVivien Didelot int dsa_switch_register_notifier(struct dsa_switch *ds) 443f515f192SVivien Didelot { 444f515f192SVivien Didelot ds->nb.notifier_call = dsa_switch_event; 445f515f192SVivien Didelot 446f515f192SVivien Didelot return raw_notifier_chain_register(&ds->dst->nh, &ds->nb); 447f515f192SVivien Didelot } 448f515f192SVivien Didelot 449f515f192SVivien Didelot void dsa_switch_unregister_notifier(struct dsa_switch *ds) 450f515f192SVivien Didelot { 451f515f192SVivien Didelot int err; 452f515f192SVivien Didelot 453f515f192SVivien Didelot err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb); 454f515f192SVivien Didelot if (err) 455f515f192SVivien Didelot dev_err(ds->dev, "failed to unregister notifier (%d)\n", err); 456f515f192SVivien Didelot } 457