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 { 52*be6ff966SVladimir Oltean return dp == info->dp || dsa_port_is_dsa(dp) || dsa_port_is_cpu(dp); 53bfcb8132SVladimir Oltean } 54bfcb8132SVladimir Oltean 55bfcb8132SVladimir Oltean static int dsa_switch_mtu(struct dsa_switch *ds, 56bfcb8132SVladimir Oltean struct dsa_notifier_mtu_info *info) 57bfcb8132SVladimir Oltean { 58fac6abd5SVladimir Oltean struct dsa_port *dp; 59fac6abd5SVladimir Oltean int ret; 60bfcb8132SVladimir Oltean 61bfcb8132SVladimir Oltean if (!ds->ops->port_change_mtu) 62bfcb8132SVladimir Oltean return -EOPNOTSUPP; 63bfcb8132SVladimir Oltean 64fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 65fac6abd5SVladimir Oltean if (dsa_port_mtu_match(dp, info)) { 66fac6abd5SVladimir Oltean ret = ds->ops->port_change_mtu(ds, dp->index, 67fac6abd5SVladimir Oltean info->mtu); 68bfcb8132SVladimir Oltean if (ret) 69bfcb8132SVladimir Oltean return ret; 70bfcb8132SVladimir Oltean } 71bfcb8132SVladimir Oltean } 72bfcb8132SVladimir Oltean 73bfcb8132SVladimir Oltean return 0; 74bfcb8132SVladimir Oltean } 75bfcb8132SVladimir Oltean 7604d3a4c6SVivien Didelot static int dsa_switch_bridge_join(struct dsa_switch *ds, 7704d3a4c6SVivien Didelot struct dsa_notifier_bridge_info *info) 7804d3a4c6SVivien Didelot { 79e19cc13cSVladimir Oltean int err; 80f66a6a69SVladimir Oltean 81726816a1SVladimir Oltean if (info->dp->ds == ds) { 8267b5fb5dSVladimir Oltean if (!ds->ops->port_bridge_join) 8367b5fb5dSVladimir Oltean return -EOPNOTSUPP; 8467b5fb5dSVladimir Oltean 85726816a1SVladimir Oltean err = ds->ops->port_bridge_join(ds, info->dp->index, 86726816a1SVladimir Oltean info->bridge, 8706b9cce4SVladimir Oltean &info->tx_fwd_offload, 8806b9cce4SVladimir Oltean info->extack); 89e19cc13cSVladimir Oltean if (err) 90e19cc13cSVladimir Oltean return err; 91e19cc13cSVladimir Oltean } 9204d3a4c6SVivien Didelot 93726816a1SVladimir Oltean if (info->dp->ds != ds && ds->ops->crosschip_bridge_join) { 94726816a1SVladimir Oltean err = ds->ops->crosschip_bridge_join(ds, 95726816a1SVladimir Oltean info->dp->ds->dst->index, 96726816a1SVladimir Oltean info->dp->ds->index, 97726816a1SVladimir Oltean info->dp->index, 98726816a1SVladimir Oltean info->bridge, 9906b9cce4SVladimir Oltean info->extack); 100e19cc13cSVladimir Oltean if (err) 101e19cc13cSVladimir Oltean return err; 102e19cc13cSVladimir Oltean } 10304d3a4c6SVivien Didelot 10491495f21SVladimir Oltean return 0; 10504d3a4c6SVivien Didelot } 10604d3a4c6SVivien Didelot 107381a7301STobias Waldekranz static int dsa_switch_bridge_leave(struct dsa_switch *ds, 108381a7301STobias Waldekranz struct dsa_notifier_bridge_info *info) 109381a7301STobias Waldekranz { 110726816a1SVladimir Oltean if (info->dp->ds == ds && ds->ops->port_bridge_leave) 111726816a1SVladimir Oltean ds->ops->port_bridge_leave(ds, info->dp->index, info->bridge); 112381a7301STobias Waldekranz 113726816a1SVladimir Oltean if (info->dp->ds != ds && ds->ops->crosschip_bridge_leave) 114726816a1SVladimir Oltean ds->ops->crosschip_bridge_leave(ds, info->dp->ds->dst->index, 115726816a1SVladimir Oltean info->dp->ds->index, 116726816a1SVladimir Oltean info->dp->index, 117381a7301STobias Waldekranz info->bridge); 118381a7301STobias Waldekranz 11991495f21SVladimir Oltean return 0; 12004d3a4c6SVivien Didelot } 12104d3a4c6SVivien Didelot 122b8e997c4SVladimir Oltean /* Matches for all upstream-facing ports (the CPU port and all upstream-facing 123b8e997c4SVladimir Oltean * DSA links) that sit between the targeted port on which the notifier was 124b8e997c4SVladimir Oltean * emitted and its dedicated CPU port. 125b8e997c4SVladimir Oltean */ 126fac6abd5SVladimir Oltean static bool dsa_port_host_address_match(struct dsa_port *dp, 127726816a1SVladimir Oltean const struct dsa_port *targeted_dp) 128b8e997c4SVladimir Oltean { 129726816a1SVladimir Oltean struct dsa_port *cpu_dp = targeted_dp->cpu_dp; 130b8e997c4SVladimir Oltean 131726816a1SVladimir Oltean if (dsa_switch_is_upstream_of(dp->ds, targeted_dp->ds)) 132fac6abd5SVladimir Oltean return dp->index == dsa_towards_port(dp->ds, cpu_dp->ds->index, 133b8e997c4SVladimir Oltean cpu_dp->index); 134b8e997c4SVladimir Oltean 135b8e997c4SVladimir Oltean return false; 136b8e997c4SVladimir Oltean } 137b8e997c4SVladimir Oltean 138161ca59dSVladimir Oltean static struct dsa_mac_addr *dsa_mac_addr_find(struct list_head *addr_list, 139c2693363SVladimir Oltean const unsigned char *addr, u16 vid, 140c2693363SVladimir Oltean struct dsa_db db) 141161ca59dSVladimir Oltean { 142161ca59dSVladimir Oltean struct dsa_mac_addr *a; 143161ca59dSVladimir Oltean 144161ca59dSVladimir Oltean list_for_each_entry(a, addr_list, list) 145c2693363SVladimir Oltean if (ether_addr_equal(a->addr, addr) && a->vid == vid && 146c2693363SVladimir Oltean dsa_db_equal(&a->db, &db)) 147161ca59dSVladimir Oltean return a; 148161ca59dSVladimir Oltean 149161ca59dSVladimir Oltean return NULL; 150161ca59dSVladimir Oltean } 151161ca59dSVladimir Oltean 152fac6abd5SVladimir Oltean static int dsa_port_do_mdb_add(struct dsa_port *dp, 153c2693363SVladimir Oltean const struct switchdev_obj_port_mdb *mdb, 154c2693363SVladimir Oltean struct dsa_db db) 155161ca59dSVladimir Oltean { 156fac6abd5SVladimir Oltean struct dsa_switch *ds = dp->ds; 157161ca59dSVladimir Oltean struct dsa_mac_addr *a; 158fac6abd5SVladimir Oltean int port = dp->index; 159338a3a47SVladimir Oltean int err = 0; 160161ca59dSVladimir Oltean 161161ca59dSVladimir Oltean /* No need to bother with refcounting for user ports */ 162161ca59dSVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 163c2693363SVladimir Oltean return ds->ops->port_mdb_add(ds, port, mdb, db); 164161ca59dSVladimir Oltean 165338a3a47SVladimir Oltean mutex_lock(&dp->addr_lists_lock); 166338a3a47SVladimir Oltean 167c2693363SVladimir Oltean a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid, db); 168161ca59dSVladimir Oltean if (a) { 169161ca59dSVladimir Oltean refcount_inc(&a->refcount); 170338a3a47SVladimir Oltean goto out; 171161ca59dSVladimir Oltean } 172161ca59dSVladimir Oltean 173161ca59dSVladimir Oltean a = kzalloc(sizeof(*a), GFP_KERNEL); 174338a3a47SVladimir Oltean if (!a) { 175338a3a47SVladimir Oltean err = -ENOMEM; 176338a3a47SVladimir Oltean goto out; 177338a3a47SVladimir Oltean } 178161ca59dSVladimir Oltean 179c2693363SVladimir Oltean err = ds->ops->port_mdb_add(ds, port, mdb, db); 180161ca59dSVladimir Oltean if (err) { 181161ca59dSVladimir Oltean kfree(a); 182338a3a47SVladimir Oltean goto out; 183161ca59dSVladimir Oltean } 184161ca59dSVladimir Oltean 185161ca59dSVladimir Oltean ether_addr_copy(a->addr, mdb->addr); 186161ca59dSVladimir Oltean a->vid = mdb->vid; 187c2693363SVladimir Oltean a->db = db; 188161ca59dSVladimir Oltean refcount_set(&a->refcount, 1); 189161ca59dSVladimir Oltean list_add_tail(&a->list, &dp->mdbs); 190161ca59dSVladimir Oltean 191338a3a47SVladimir Oltean out: 192338a3a47SVladimir Oltean mutex_unlock(&dp->addr_lists_lock); 193338a3a47SVladimir Oltean 194338a3a47SVladimir Oltean return err; 195161ca59dSVladimir Oltean } 196161ca59dSVladimir Oltean 197fac6abd5SVladimir Oltean static int dsa_port_do_mdb_del(struct dsa_port *dp, 198c2693363SVladimir Oltean const struct switchdev_obj_port_mdb *mdb, 199c2693363SVladimir Oltean struct dsa_db db) 200161ca59dSVladimir Oltean { 201fac6abd5SVladimir Oltean struct dsa_switch *ds = dp->ds; 202161ca59dSVladimir Oltean struct dsa_mac_addr *a; 203fac6abd5SVladimir Oltean int port = dp->index; 204338a3a47SVladimir Oltean int err = 0; 205161ca59dSVladimir Oltean 206161ca59dSVladimir Oltean /* No need to bother with refcounting for user ports */ 207161ca59dSVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 208c2693363SVladimir Oltean return ds->ops->port_mdb_del(ds, port, mdb, db); 209161ca59dSVladimir Oltean 210338a3a47SVladimir Oltean mutex_lock(&dp->addr_lists_lock); 211338a3a47SVladimir Oltean 212c2693363SVladimir Oltean a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid, db); 213338a3a47SVladimir Oltean if (!a) { 214338a3a47SVladimir Oltean err = -ENOENT; 215338a3a47SVladimir Oltean goto out; 216338a3a47SVladimir Oltean } 217161ca59dSVladimir Oltean 218161ca59dSVladimir Oltean if (!refcount_dec_and_test(&a->refcount)) 219338a3a47SVladimir Oltean goto out; 220161ca59dSVladimir Oltean 221c2693363SVladimir Oltean err = ds->ops->port_mdb_del(ds, port, mdb, db); 222161ca59dSVladimir Oltean if (err) { 223232deb3fSVladimir Oltean refcount_set(&a->refcount, 1); 224338a3a47SVladimir Oltean goto out; 225161ca59dSVladimir Oltean } 226161ca59dSVladimir Oltean 227161ca59dSVladimir Oltean list_del(&a->list); 228161ca59dSVladimir Oltean kfree(a); 229161ca59dSVladimir Oltean 230338a3a47SVladimir Oltean out: 231338a3a47SVladimir Oltean mutex_unlock(&dp->addr_lists_lock); 232338a3a47SVladimir Oltean 233338a3a47SVladimir Oltean return err; 234161ca59dSVladimir Oltean } 235161ca59dSVladimir Oltean 236fac6abd5SVladimir Oltean static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr, 237c2693363SVladimir Oltean u16 vid, struct dsa_db db) 2383f6e32f9SVladimir Oltean { 239fac6abd5SVladimir Oltean struct dsa_switch *ds = dp->ds; 2403f6e32f9SVladimir Oltean struct dsa_mac_addr *a; 241fac6abd5SVladimir Oltean int port = dp->index; 242338a3a47SVladimir Oltean int err = 0; 2433f6e32f9SVladimir Oltean 2443f6e32f9SVladimir Oltean /* No need to bother with refcounting for user ports */ 2453f6e32f9SVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 246c2693363SVladimir Oltean return ds->ops->port_fdb_add(ds, port, addr, vid, db); 2473f6e32f9SVladimir Oltean 248338a3a47SVladimir Oltean mutex_lock(&dp->addr_lists_lock); 249338a3a47SVladimir Oltean 250c2693363SVladimir Oltean a = dsa_mac_addr_find(&dp->fdbs, addr, vid, db); 2513f6e32f9SVladimir Oltean if (a) { 2523f6e32f9SVladimir Oltean refcount_inc(&a->refcount); 253338a3a47SVladimir Oltean goto out; 2543f6e32f9SVladimir Oltean } 2553f6e32f9SVladimir Oltean 2563f6e32f9SVladimir Oltean a = kzalloc(sizeof(*a), GFP_KERNEL); 257338a3a47SVladimir Oltean if (!a) { 258338a3a47SVladimir Oltean err = -ENOMEM; 259338a3a47SVladimir Oltean goto out; 260338a3a47SVladimir Oltean } 2613f6e32f9SVladimir Oltean 262c2693363SVladimir Oltean err = ds->ops->port_fdb_add(ds, port, addr, vid, db); 2633f6e32f9SVladimir Oltean if (err) { 2643f6e32f9SVladimir Oltean kfree(a); 265338a3a47SVladimir Oltean goto out; 2663f6e32f9SVladimir Oltean } 2673f6e32f9SVladimir Oltean 2683f6e32f9SVladimir Oltean ether_addr_copy(a->addr, addr); 2693f6e32f9SVladimir Oltean a->vid = vid; 270c2693363SVladimir Oltean a->db = db; 2713f6e32f9SVladimir Oltean refcount_set(&a->refcount, 1); 2723f6e32f9SVladimir Oltean list_add_tail(&a->list, &dp->fdbs); 2733f6e32f9SVladimir Oltean 274338a3a47SVladimir Oltean out: 275338a3a47SVladimir Oltean mutex_unlock(&dp->addr_lists_lock); 276338a3a47SVladimir Oltean 277338a3a47SVladimir Oltean return err; 2783f6e32f9SVladimir Oltean } 2793f6e32f9SVladimir Oltean 280fac6abd5SVladimir Oltean static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr, 281c2693363SVladimir Oltean u16 vid, struct dsa_db db) 2823f6e32f9SVladimir Oltean { 283fac6abd5SVladimir Oltean struct dsa_switch *ds = dp->ds; 2843f6e32f9SVladimir Oltean struct dsa_mac_addr *a; 285fac6abd5SVladimir Oltean int port = dp->index; 286338a3a47SVladimir Oltean int err = 0; 2873f6e32f9SVladimir Oltean 2883f6e32f9SVladimir Oltean /* No need to bother with refcounting for user ports */ 2893f6e32f9SVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 290c2693363SVladimir Oltean return ds->ops->port_fdb_del(ds, port, addr, vid, db); 2913f6e32f9SVladimir Oltean 292338a3a47SVladimir Oltean mutex_lock(&dp->addr_lists_lock); 293338a3a47SVladimir Oltean 294c2693363SVladimir Oltean a = dsa_mac_addr_find(&dp->fdbs, addr, vid, db); 295338a3a47SVladimir Oltean if (!a) { 296338a3a47SVladimir Oltean err = -ENOENT; 297338a3a47SVladimir Oltean goto out; 298338a3a47SVladimir Oltean } 2993f6e32f9SVladimir Oltean 3003f6e32f9SVladimir Oltean if (!refcount_dec_and_test(&a->refcount)) 301338a3a47SVladimir Oltean goto out; 3023f6e32f9SVladimir Oltean 303c2693363SVladimir Oltean err = ds->ops->port_fdb_del(ds, port, addr, vid, db); 3043f6e32f9SVladimir Oltean if (err) { 305232deb3fSVladimir Oltean refcount_set(&a->refcount, 1); 306338a3a47SVladimir Oltean goto out; 3073f6e32f9SVladimir Oltean } 3083f6e32f9SVladimir Oltean 3093f6e32f9SVladimir Oltean list_del(&a->list); 3103f6e32f9SVladimir Oltean kfree(a); 3113f6e32f9SVladimir Oltean 312338a3a47SVladimir Oltean out: 313338a3a47SVladimir Oltean mutex_unlock(&dp->addr_lists_lock); 314338a3a47SVladimir Oltean 315338a3a47SVladimir Oltean return err; 3163f6e32f9SVladimir Oltean } 3173f6e32f9SVladimir Oltean 318e212fa7cSVladimir Oltean static int dsa_switch_do_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag *lag, 319c2693363SVladimir Oltean const unsigned char *addr, u16 vid, 320c2693363SVladimir Oltean struct dsa_db db) 321e212fa7cSVladimir Oltean { 322e212fa7cSVladimir Oltean struct dsa_mac_addr *a; 323e212fa7cSVladimir Oltean int err = 0; 324e212fa7cSVladimir Oltean 325e212fa7cSVladimir Oltean mutex_lock(&lag->fdb_lock); 326e212fa7cSVladimir Oltean 327c2693363SVladimir Oltean a = dsa_mac_addr_find(&lag->fdbs, addr, vid, db); 328e212fa7cSVladimir Oltean if (a) { 329e212fa7cSVladimir Oltean refcount_inc(&a->refcount); 330e212fa7cSVladimir Oltean goto out; 331e212fa7cSVladimir Oltean } 332e212fa7cSVladimir Oltean 333e212fa7cSVladimir Oltean a = kzalloc(sizeof(*a), GFP_KERNEL); 334e212fa7cSVladimir Oltean if (!a) { 335e212fa7cSVladimir Oltean err = -ENOMEM; 336e212fa7cSVladimir Oltean goto out; 337e212fa7cSVladimir Oltean } 338e212fa7cSVladimir Oltean 339c2693363SVladimir Oltean err = ds->ops->lag_fdb_add(ds, *lag, addr, vid, db); 340e212fa7cSVladimir Oltean if (err) { 341e212fa7cSVladimir Oltean kfree(a); 342e212fa7cSVladimir Oltean goto out; 343e212fa7cSVladimir Oltean } 344e212fa7cSVladimir Oltean 345e212fa7cSVladimir Oltean ether_addr_copy(a->addr, addr); 346e212fa7cSVladimir Oltean a->vid = vid; 347e212fa7cSVladimir Oltean refcount_set(&a->refcount, 1); 348e212fa7cSVladimir Oltean list_add_tail(&a->list, &lag->fdbs); 349e212fa7cSVladimir Oltean 350e212fa7cSVladimir Oltean out: 351e212fa7cSVladimir Oltean mutex_unlock(&lag->fdb_lock); 352e212fa7cSVladimir Oltean 353e212fa7cSVladimir Oltean return err; 354e212fa7cSVladimir Oltean } 355e212fa7cSVladimir Oltean 356e212fa7cSVladimir Oltean static int dsa_switch_do_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag *lag, 357c2693363SVladimir Oltean const unsigned char *addr, u16 vid, 358c2693363SVladimir Oltean struct dsa_db db) 359e212fa7cSVladimir Oltean { 360e212fa7cSVladimir Oltean struct dsa_mac_addr *a; 361e212fa7cSVladimir Oltean int err = 0; 362e212fa7cSVladimir Oltean 363e212fa7cSVladimir Oltean mutex_lock(&lag->fdb_lock); 364e212fa7cSVladimir Oltean 365c2693363SVladimir Oltean a = dsa_mac_addr_find(&lag->fdbs, addr, vid, db); 366e212fa7cSVladimir Oltean if (!a) { 367e212fa7cSVladimir Oltean err = -ENOENT; 368e212fa7cSVladimir Oltean goto out; 369e212fa7cSVladimir Oltean } 370e212fa7cSVladimir Oltean 371e212fa7cSVladimir Oltean if (!refcount_dec_and_test(&a->refcount)) 372e212fa7cSVladimir Oltean goto out; 373e212fa7cSVladimir Oltean 374c2693363SVladimir Oltean err = ds->ops->lag_fdb_del(ds, *lag, addr, vid, db); 375e212fa7cSVladimir Oltean if (err) { 376e212fa7cSVladimir Oltean refcount_set(&a->refcount, 1); 377e212fa7cSVladimir Oltean goto out; 378e212fa7cSVladimir Oltean } 379e212fa7cSVladimir Oltean 380e212fa7cSVladimir Oltean list_del(&a->list); 381e212fa7cSVladimir Oltean kfree(a); 382e212fa7cSVladimir Oltean 383e212fa7cSVladimir Oltean out: 384e212fa7cSVladimir Oltean mutex_unlock(&lag->fdb_lock); 385e212fa7cSVladimir Oltean 386e212fa7cSVladimir Oltean return err; 387e212fa7cSVladimir Oltean } 388e212fa7cSVladimir Oltean 3893dc80afcSVladimir Oltean static int dsa_switch_host_fdb_add(struct dsa_switch *ds, 3903dc80afcSVladimir Oltean struct dsa_notifier_fdb_info *info) 3913dc80afcSVladimir Oltean { 392fac6abd5SVladimir Oltean struct dsa_port *dp; 3933dc80afcSVladimir Oltean int err = 0; 3943dc80afcSVladimir Oltean 3953dc80afcSVladimir Oltean if (!ds->ops->port_fdb_add) 3963dc80afcSVladimir Oltean return -EOPNOTSUPP; 3973dc80afcSVladimir Oltean 398fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 399726816a1SVladimir Oltean if (dsa_port_host_address_match(dp, info->dp)) { 400c2693363SVladimir Oltean err = dsa_port_do_fdb_add(dp, info->addr, info->vid, 401c2693363SVladimir Oltean info->db); 4023dc80afcSVladimir Oltean if (err) 4033dc80afcSVladimir Oltean break; 4043dc80afcSVladimir Oltean } 4053dc80afcSVladimir Oltean } 4063dc80afcSVladimir Oltean 4073dc80afcSVladimir Oltean return err; 4083dc80afcSVladimir Oltean } 4093dc80afcSVladimir Oltean 4103dc80afcSVladimir Oltean static int dsa_switch_host_fdb_del(struct dsa_switch *ds, 4113dc80afcSVladimir Oltean struct dsa_notifier_fdb_info *info) 4123dc80afcSVladimir Oltean { 413fac6abd5SVladimir Oltean struct dsa_port *dp; 4143f6e32f9SVladimir Oltean int err = 0; 4153f6e32f9SVladimir Oltean 4163dc80afcSVladimir Oltean if (!ds->ops->port_fdb_del) 4173dc80afcSVladimir Oltean return -EOPNOTSUPP; 4183dc80afcSVladimir Oltean 419fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 420726816a1SVladimir Oltean if (dsa_port_host_address_match(dp, info->dp)) { 421c2693363SVladimir Oltean err = dsa_port_do_fdb_del(dp, info->addr, info->vid, 422c2693363SVladimir Oltean info->db); 4233f6e32f9SVladimir Oltean if (err) 4243f6e32f9SVladimir Oltean break; 4253f6e32f9SVladimir Oltean } 4263f6e32f9SVladimir Oltean } 4273dc80afcSVladimir Oltean 4283f6e32f9SVladimir Oltean return err; 4293dc80afcSVladimir Oltean } 4303dc80afcSVladimir Oltean 431685fb6a4SVivien Didelot static int dsa_switch_fdb_add(struct dsa_switch *ds, 432685fb6a4SVivien Didelot struct dsa_notifier_fdb_info *info) 433685fb6a4SVivien Didelot { 434726816a1SVladimir Oltean int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index); 435fac6abd5SVladimir Oltean struct dsa_port *dp = dsa_to_port(ds, port); 436685fb6a4SVivien Didelot 4371b6dd556SArkadi Sharshevsky if (!ds->ops->port_fdb_add) 438685fb6a4SVivien Didelot return -EOPNOTSUPP; 439685fb6a4SVivien Didelot 440c2693363SVladimir Oltean return dsa_port_do_fdb_add(dp, info->addr, info->vid, info->db); 441685fb6a4SVivien Didelot } 442685fb6a4SVivien Didelot 443685fb6a4SVivien Didelot static int dsa_switch_fdb_del(struct dsa_switch *ds, 444685fb6a4SVivien Didelot struct dsa_notifier_fdb_info *info) 445685fb6a4SVivien Didelot { 446726816a1SVladimir Oltean int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index); 447fac6abd5SVladimir Oltean struct dsa_port *dp = dsa_to_port(ds, port); 448685fb6a4SVivien Didelot 449685fb6a4SVivien Didelot if (!ds->ops->port_fdb_del) 450685fb6a4SVivien Didelot return -EOPNOTSUPP; 451685fb6a4SVivien Didelot 452c2693363SVladimir Oltean return dsa_port_do_fdb_del(dp, info->addr, info->vid, info->db); 453685fb6a4SVivien Didelot } 454685fb6a4SVivien Didelot 455e212fa7cSVladimir Oltean static int dsa_switch_lag_fdb_add(struct dsa_switch *ds, 456e212fa7cSVladimir Oltean struct dsa_notifier_lag_fdb_info *info) 457e212fa7cSVladimir Oltean { 458e212fa7cSVladimir Oltean struct dsa_port *dp; 459e212fa7cSVladimir Oltean 460e212fa7cSVladimir Oltean if (!ds->ops->lag_fdb_add) 461e212fa7cSVladimir Oltean return -EOPNOTSUPP; 462e212fa7cSVladimir Oltean 463e212fa7cSVladimir Oltean /* Notify switch only if it has a port in this LAG */ 464e212fa7cSVladimir Oltean dsa_switch_for_each_port(dp, ds) 465e212fa7cSVladimir Oltean if (dsa_port_offloads_lag(dp, info->lag)) 466e212fa7cSVladimir Oltean return dsa_switch_do_lag_fdb_add(ds, info->lag, 467c2693363SVladimir Oltean info->addr, info->vid, 468c2693363SVladimir Oltean info->db); 469e212fa7cSVladimir Oltean 470e212fa7cSVladimir Oltean return 0; 471e212fa7cSVladimir Oltean } 472e212fa7cSVladimir Oltean 473e212fa7cSVladimir Oltean static int dsa_switch_lag_fdb_del(struct dsa_switch *ds, 474e212fa7cSVladimir Oltean struct dsa_notifier_lag_fdb_info *info) 475e212fa7cSVladimir Oltean { 476e212fa7cSVladimir Oltean struct dsa_port *dp; 477e212fa7cSVladimir Oltean 478e212fa7cSVladimir Oltean if (!ds->ops->lag_fdb_del) 479e212fa7cSVladimir Oltean return -EOPNOTSUPP; 480e212fa7cSVladimir Oltean 481e212fa7cSVladimir Oltean /* Notify switch only if it has a port in this LAG */ 482e212fa7cSVladimir Oltean dsa_switch_for_each_port(dp, ds) 483e212fa7cSVladimir Oltean if (dsa_port_offloads_lag(dp, info->lag)) 484e212fa7cSVladimir Oltean return dsa_switch_do_lag_fdb_del(ds, info->lag, 485c2693363SVladimir Oltean info->addr, info->vid, 486c2693363SVladimir Oltean info->db); 487e212fa7cSVladimir Oltean 488e212fa7cSVladimir Oltean return 0; 489e212fa7cSVladimir Oltean } 490e212fa7cSVladimir Oltean 491058102a6STobias Waldekranz static int dsa_switch_lag_change(struct dsa_switch *ds, 492058102a6STobias Waldekranz struct dsa_notifier_lag_info *info) 493058102a6STobias Waldekranz { 494726816a1SVladimir Oltean if (info->dp->ds == ds && ds->ops->port_lag_change) 495726816a1SVladimir Oltean return ds->ops->port_lag_change(ds, info->dp->index); 496058102a6STobias Waldekranz 497726816a1SVladimir Oltean if (info->dp->ds != ds && ds->ops->crosschip_lag_change) 498726816a1SVladimir Oltean return ds->ops->crosschip_lag_change(ds, info->dp->ds->index, 499726816a1SVladimir Oltean info->dp->index); 500058102a6STobias Waldekranz 501058102a6STobias Waldekranz return 0; 502058102a6STobias Waldekranz } 503058102a6STobias Waldekranz 504058102a6STobias Waldekranz static int dsa_switch_lag_join(struct dsa_switch *ds, 505058102a6STobias Waldekranz struct dsa_notifier_lag_info *info) 506058102a6STobias Waldekranz { 507726816a1SVladimir Oltean if (info->dp->ds == ds && ds->ops->port_lag_join) 508726816a1SVladimir Oltean return ds->ops->port_lag_join(ds, info->dp->index, info->lag, 509058102a6STobias Waldekranz info->info); 510058102a6STobias Waldekranz 511726816a1SVladimir Oltean if (info->dp->ds != ds && ds->ops->crosschip_lag_join) 512726816a1SVladimir Oltean return ds->ops->crosschip_lag_join(ds, info->dp->ds->index, 513726816a1SVladimir Oltean info->dp->index, info->lag, 514058102a6STobias Waldekranz info->info); 515058102a6STobias Waldekranz 516b71d0987SVladimir Oltean return -EOPNOTSUPP; 517058102a6STobias Waldekranz } 518058102a6STobias Waldekranz 519058102a6STobias Waldekranz static int dsa_switch_lag_leave(struct dsa_switch *ds, 520058102a6STobias Waldekranz struct dsa_notifier_lag_info *info) 521058102a6STobias Waldekranz { 522726816a1SVladimir Oltean if (info->dp->ds == ds && ds->ops->port_lag_leave) 523726816a1SVladimir Oltean return ds->ops->port_lag_leave(ds, info->dp->index, info->lag); 524058102a6STobias Waldekranz 525726816a1SVladimir Oltean if (info->dp->ds != ds && ds->ops->crosschip_lag_leave) 526726816a1SVladimir Oltean return ds->ops->crosschip_lag_leave(ds, info->dp->ds->index, 527726816a1SVladimir Oltean info->dp->index, info->lag); 528058102a6STobias Waldekranz 529b71d0987SVladimir Oltean return -EOPNOTSUPP; 530058102a6STobias Waldekranz } 531058102a6STobias Waldekranz 532ffb68fc5SVladimir Oltean static int dsa_switch_mdb_add(struct dsa_switch *ds, 533e65d45ccSVivien Didelot struct dsa_notifier_mdb_info *info) 534e6db98dbSVivien Didelot { 535726816a1SVladimir Oltean int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index); 536fac6abd5SVladimir Oltean struct dsa_port *dp = dsa_to_port(ds, port); 537e6db98dbSVivien Didelot 538a52b2da7SVladimir Oltean if (!ds->ops->port_mdb_add) 539e6db98dbSVivien Didelot return -EOPNOTSUPP; 540e6db98dbSVivien Didelot 541c2693363SVladimir Oltean return dsa_port_do_mdb_add(dp, info->mdb, info->db); 542e6db98dbSVivien Didelot } 5438ae5bcdcSVivien Didelot 5448ae5bcdcSVivien Didelot static int dsa_switch_mdb_del(struct dsa_switch *ds, 5458ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info *info) 5468ae5bcdcSVivien Didelot { 547726816a1SVladimir Oltean int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index); 548fac6abd5SVladimir Oltean struct dsa_port *dp = dsa_to_port(ds, port); 549161ca59dSVladimir Oltean 5508ae5bcdcSVivien Didelot if (!ds->ops->port_mdb_del) 5518ae5bcdcSVivien Didelot return -EOPNOTSUPP; 5528ae5bcdcSVivien Didelot 553c2693363SVladimir Oltean return dsa_port_do_mdb_del(dp, info->mdb, info->db); 5548ae5bcdcSVivien Didelot } 5558ae5bcdcSVivien Didelot 556b8e997c4SVladimir Oltean static int dsa_switch_host_mdb_add(struct dsa_switch *ds, 557b8e997c4SVladimir Oltean struct dsa_notifier_mdb_info *info) 558b8e997c4SVladimir Oltean { 559fac6abd5SVladimir Oltean struct dsa_port *dp; 560b8e997c4SVladimir Oltean int err = 0; 561b8e997c4SVladimir Oltean 562b8e997c4SVladimir Oltean if (!ds->ops->port_mdb_add) 563b8e997c4SVladimir Oltean return -EOPNOTSUPP; 564b8e997c4SVladimir Oltean 565fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 566726816a1SVladimir Oltean if (dsa_port_host_address_match(dp, info->dp)) { 567c2693363SVladimir Oltean err = dsa_port_do_mdb_add(dp, info->mdb, info->db); 568b8e997c4SVladimir Oltean if (err) 569b8e997c4SVladimir Oltean break; 570b8e997c4SVladimir Oltean } 571b8e997c4SVladimir Oltean } 572b8e997c4SVladimir Oltean 573b8e997c4SVladimir Oltean return err; 574b8e997c4SVladimir Oltean } 575b8e997c4SVladimir Oltean 576b8e997c4SVladimir Oltean static int dsa_switch_host_mdb_del(struct dsa_switch *ds, 577b8e997c4SVladimir Oltean struct dsa_notifier_mdb_info *info) 578b8e997c4SVladimir Oltean { 579fac6abd5SVladimir Oltean struct dsa_port *dp; 580161ca59dSVladimir Oltean int err = 0; 581161ca59dSVladimir Oltean 582b8e997c4SVladimir Oltean if (!ds->ops->port_mdb_del) 583b8e997c4SVladimir Oltean return -EOPNOTSUPP; 584b8e997c4SVladimir Oltean 585fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 586726816a1SVladimir Oltean if (dsa_port_host_address_match(dp, info->dp)) { 587c2693363SVladimir Oltean err = dsa_port_do_mdb_del(dp, info->mdb, info->db); 588161ca59dSVladimir Oltean if (err) 589161ca59dSVladimir Oltean break; 590161ca59dSVladimir Oltean } 591161ca59dSVladimir Oltean } 592b8e997c4SVladimir Oltean 593161ca59dSVladimir Oltean return err; 594b8e997c4SVladimir Oltean } 595b8e997c4SVladimir Oltean 596134ef238SVladimir Oltean /* Port VLANs match on the targeted port and on all DSA ports */ 597fac6abd5SVladimir Oltean static bool dsa_port_vlan_match(struct dsa_port *dp, 598e65d45ccSVivien Didelot struct dsa_notifier_vlan_info *info) 599e65d45ccSVivien Didelot { 600726816a1SVladimir Oltean return dsa_port_is_dsa(dp) || dp == info->dp; 601e65d45ccSVivien Didelot } 602e65d45ccSVivien Didelot 603134ef238SVladimir Oltean /* Host VLANs match on the targeted port's CPU port, and on all DSA ports 604134ef238SVladimir Oltean * (upstream and downstream) of that switch and its upstream switches. 605134ef238SVladimir Oltean */ 606134ef238SVladimir Oltean static bool dsa_port_host_vlan_match(struct dsa_port *dp, 607726816a1SVladimir Oltean const struct dsa_port *targeted_dp) 608134ef238SVladimir Oltean { 609726816a1SVladimir Oltean struct dsa_port *cpu_dp = targeted_dp->cpu_dp; 610134ef238SVladimir Oltean 611726816a1SVladimir Oltean if (dsa_switch_is_upstream_of(dp->ds, targeted_dp->ds)) 612134ef238SVladimir Oltean return dsa_port_is_dsa(dp) || dp == cpu_dp; 613134ef238SVladimir Oltean 614134ef238SVladimir Oltean return false; 615134ef238SVladimir Oltean } 616134ef238SVladimir Oltean 617134ef238SVladimir Oltean static struct dsa_vlan *dsa_vlan_find(struct list_head *vlan_list, 618134ef238SVladimir Oltean const struct switchdev_obj_port_vlan *vlan) 619134ef238SVladimir Oltean { 620134ef238SVladimir Oltean struct dsa_vlan *v; 621134ef238SVladimir Oltean 622134ef238SVladimir Oltean list_for_each_entry(v, vlan_list, list) 623134ef238SVladimir Oltean if (v->vid == vlan->vid) 624134ef238SVladimir Oltean return v; 625134ef238SVladimir Oltean 626134ef238SVladimir Oltean return NULL; 627134ef238SVladimir Oltean } 628134ef238SVladimir Oltean 629134ef238SVladimir Oltean static int dsa_port_do_vlan_add(struct dsa_port *dp, 630134ef238SVladimir Oltean const struct switchdev_obj_port_vlan *vlan, 631134ef238SVladimir Oltean struct netlink_ext_ack *extack) 632134ef238SVladimir Oltean { 633134ef238SVladimir Oltean struct dsa_switch *ds = dp->ds; 634134ef238SVladimir Oltean int port = dp->index; 635134ef238SVladimir Oltean struct dsa_vlan *v; 636134ef238SVladimir Oltean int err = 0; 637134ef238SVladimir Oltean 638134ef238SVladimir Oltean /* No need to bother with refcounting for user ports. */ 639134ef238SVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 640134ef238SVladimir Oltean return ds->ops->port_vlan_add(ds, port, vlan, extack); 641134ef238SVladimir Oltean 642134ef238SVladimir Oltean /* No need to propagate on shared ports the existing VLANs that were 643134ef238SVladimir Oltean * re-notified after just the flags have changed. This would cause a 644134ef238SVladimir Oltean * refcount bump which we need to avoid, since it unbalances the 645134ef238SVladimir Oltean * additions with the deletions. 646134ef238SVladimir Oltean */ 647134ef238SVladimir Oltean if (vlan->changed) 648134ef238SVladimir Oltean return 0; 649134ef238SVladimir Oltean 650134ef238SVladimir Oltean mutex_lock(&dp->vlans_lock); 651134ef238SVladimir Oltean 652134ef238SVladimir Oltean v = dsa_vlan_find(&dp->vlans, vlan); 653134ef238SVladimir Oltean if (v) { 654134ef238SVladimir Oltean refcount_inc(&v->refcount); 655134ef238SVladimir Oltean goto out; 656134ef238SVladimir Oltean } 657134ef238SVladimir Oltean 658134ef238SVladimir Oltean v = kzalloc(sizeof(*v), GFP_KERNEL); 659134ef238SVladimir Oltean if (!v) { 660134ef238SVladimir Oltean err = -ENOMEM; 661134ef238SVladimir Oltean goto out; 662134ef238SVladimir Oltean } 663134ef238SVladimir Oltean 664134ef238SVladimir Oltean err = ds->ops->port_vlan_add(ds, port, vlan, extack); 665134ef238SVladimir Oltean if (err) { 666134ef238SVladimir Oltean kfree(v); 667134ef238SVladimir Oltean goto out; 668134ef238SVladimir Oltean } 669134ef238SVladimir Oltean 670134ef238SVladimir Oltean v->vid = vlan->vid; 671134ef238SVladimir Oltean refcount_set(&v->refcount, 1); 672134ef238SVladimir Oltean list_add_tail(&v->list, &dp->vlans); 673134ef238SVladimir Oltean 674134ef238SVladimir Oltean out: 675134ef238SVladimir Oltean mutex_unlock(&dp->vlans_lock); 676134ef238SVladimir Oltean 677134ef238SVladimir Oltean return err; 678134ef238SVladimir Oltean } 679134ef238SVladimir Oltean 680134ef238SVladimir Oltean static int dsa_port_do_vlan_del(struct dsa_port *dp, 681134ef238SVladimir Oltean const struct switchdev_obj_port_vlan *vlan) 682134ef238SVladimir Oltean { 683134ef238SVladimir Oltean struct dsa_switch *ds = dp->ds; 684134ef238SVladimir Oltean int port = dp->index; 685134ef238SVladimir Oltean struct dsa_vlan *v; 686134ef238SVladimir Oltean int err = 0; 687134ef238SVladimir Oltean 688134ef238SVladimir Oltean /* No need to bother with refcounting for user ports */ 689134ef238SVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 690134ef238SVladimir Oltean return ds->ops->port_vlan_del(ds, port, vlan); 691134ef238SVladimir Oltean 692134ef238SVladimir Oltean mutex_lock(&dp->vlans_lock); 693134ef238SVladimir Oltean 694134ef238SVladimir Oltean v = dsa_vlan_find(&dp->vlans, vlan); 695134ef238SVladimir Oltean if (!v) { 696134ef238SVladimir Oltean err = -ENOENT; 697134ef238SVladimir Oltean goto out; 698134ef238SVladimir Oltean } 699134ef238SVladimir Oltean 700134ef238SVladimir Oltean if (!refcount_dec_and_test(&v->refcount)) 701134ef238SVladimir Oltean goto out; 702134ef238SVladimir Oltean 703134ef238SVladimir Oltean err = ds->ops->port_vlan_del(ds, port, vlan); 704134ef238SVladimir Oltean if (err) { 705134ef238SVladimir Oltean refcount_set(&v->refcount, 1); 706134ef238SVladimir Oltean goto out; 707134ef238SVladimir Oltean } 708134ef238SVladimir Oltean 709134ef238SVladimir Oltean list_del(&v->list); 710134ef238SVladimir Oltean kfree(v); 711134ef238SVladimir Oltean 712134ef238SVladimir Oltean out: 713134ef238SVladimir Oltean mutex_unlock(&dp->vlans_lock); 714134ef238SVladimir Oltean 715134ef238SVladimir Oltean return err; 716134ef238SVladimir Oltean } 717134ef238SVladimir Oltean 718ffb68fc5SVladimir Oltean static int dsa_switch_vlan_add(struct dsa_switch *ds, 719e65d45ccSVivien Didelot struct dsa_notifier_vlan_info *info) 7209c428c59SVivien Didelot { 721fac6abd5SVladimir Oltean struct dsa_port *dp; 722fac6abd5SVladimir Oltean int err; 7239c428c59SVivien Didelot 7241958d581SVladimir Oltean if (!ds->ops->port_vlan_add) 7259c428c59SVivien Didelot return -EOPNOTSUPP; 7269c428c59SVivien Didelot 727fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 728fac6abd5SVladimir Oltean if (dsa_port_vlan_match(dp, info)) { 729134ef238SVladimir Oltean err = dsa_port_do_vlan_add(dp, info->vlan, 73031046a5fSVladimir Oltean info->extack); 7319c428c59SVivien Didelot if (err) 7329c428c59SVivien Didelot return err; 7339c428c59SVivien Didelot } 734e65d45ccSVivien Didelot } 7359c428c59SVivien Didelot 736d0c627b8SVivien Didelot return 0; 737d0c627b8SVivien Didelot } 738d0c627b8SVivien Didelot 739d0c627b8SVivien Didelot static int dsa_switch_vlan_del(struct dsa_switch *ds, 740d0c627b8SVivien Didelot struct dsa_notifier_vlan_info *info) 741d0c627b8SVivien Didelot { 742134ef238SVladimir Oltean struct dsa_port *dp; 743134ef238SVladimir Oltean int err; 744134ef238SVladimir Oltean 745d0c627b8SVivien Didelot if (!ds->ops->port_vlan_del) 746d0c627b8SVivien Didelot return -EOPNOTSUPP; 747d0c627b8SVivien Didelot 748134ef238SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 749134ef238SVladimir Oltean if (dsa_port_vlan_match(dp, info)) { 750134ef238SVladimir Oltean err = dsa_port_do_vlan_del(dp, info->vlan); 751134ef238SVladimir Oltean if (err) 752134ef238SVladimir Oltean return err; 753134ef238SVladimir Oltean } 754134ef238SVladimir Oltean } 7551ca4aa9cSVivien Didelot 756134ef238SVladimir Oltean return 0; 757134ef238SVladimir Oltean } 758134ef238SVladimir Oltean 759134ef238SVladimir Oltean static int dsa_switch_host_vlan_add(struct dsa_switch *ds, 760134ef238SVladimir Oltean struct dsa_notifier_vlan_info *info) 761134ef238SVladimir Oltean { 762134ef238SVladimir Oltean struct dsa_port *dp; 763134ef238SVladimir Oltean int err; 764134ef238SVladimir Oltean 765134ef238SVladimir Oltean if (!ds->ops->port_vlan_add) 766134ef238SVladimir Oltean return -EOPNOTSUPP; 767134ef238SVladimir Oltean 768134ef238SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 769726816a1SVladimir Oltean if (dsa_port_host_vlan_match(dp, info->dp)) { 770134ef238SVladimir Oltean err = dsa_port_do_vlan_add(dp, info->vlan, 771134ef238SVladimir Oltean info->extack); 772134ef238SVladimir Oltean if (err) 773134ef238SVladimir Oltean return err; 774134ef238SVladimir Oltean } 775134ef238SVladimir Oltean } 776134ef238SVladimir Oltean 777134ef238SVladimir Oltean return 0; 778134ef238SVladimir Oltean } 779134ef238SVladimir Oltean 780134ef238SVladimir Oltean static int dsa_switch_host_vlan_del(struct dsa_switch *ds, 781134ef238SVladimir Oltean struct dsa_notifier_vlan_info *info) 782134ef238SVladimir Oltean { 783134ef238SVladimir Oltean struct dsa_port *dp; 784134ef238SVladimir Oltean int err; 785134ef238SVladimir Oltean 786134ef238SVladimir Oltean if (!ds->ops->port_vlan_del) 787134ef238SVladimir Oltean return -EOPNOTSUPP; 788134ef238SVladimir Oltean 789134ef238SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 790726816a1SVladimir Oltean if (dsa_port_host_vlan_match(dp, info->dp)) { 791134ef238SVladimir Oltean err = dsa_port_do_vlan_del(dp, info->vlan); 792134ef238SVladimir Oltean if (err) 793134ef238SVladimir Oltean return err; 794134ef238SVladimir Oltean } 795134ef238SVladimir Oltean } 796134ef238SVladimir Oltean 7971ca4aa9cSVivien Didelot return 0; 798d0c627b8SVivien Didelot } 799d0c627b8SVivien Didelot 80053da0ebaSVladimir Oltean static int dsa_switch_change_tag_proto(struct dsa_switch *ds, 80153da0ebaSVladimir Oltean struct dsa_notifier_tag_proto_info *info) 80253da0ebaSVladimir Oltean { 80353da0ebaSVladimir Oltean const struct dsa_device_ops *tag_ops = info->tag_ops; 804d0004a02SVladimir Oltean struct dsa_port *dp, *cpu_dp; 805d0004a02SVladimir Oltean int err; 80653da0ebaSVladimir Oltean 80753da0ebaSVladimir Oltean if (!ds->ops->change_tag_protocol) 80853da0ebaSVladimir Oltean return -EOPNOTSUPP; 80953da0ebaSVladimir Oltean 81053da0ebaSVladimir Oltean ASSERT_RTNL(); 81153da0ebaSVladimir Oltean 812d0004a02SVladimir Oltean dsa_switch_for_each_cpu_port(cpu_dp, ds) { 813d0004a02SVladimir Oltean err = ds->ops->change_tag_protocol(ds, cpu_dp->index, 814d0004a02SVladimir Oltean tag_ops->proto); 81553da0ebaSVladimir Oltean if (err) 81653da0ebaSVladimir Oltean return err; 81753da0ebaSVladimir Oltean 818d0004a02SVladimir Oltean dsa_port_set_tag_protocol(cpu_dp, tag_ops); 81953da0ebaSVladimir Oltean } 82053da0ebaSVladimir Oltean 82153da0ebaSVladimir Oltean /* Now that changing the tag protocol can no longer fail, let's update 82253da0ebaSVladimir Oltean * the remaining bits which are "duplicated for faster access", and the 82353da0ebaSVladimir Oltean * bits that depend on the tagger, such as the MTU. 82453da0ebaSVladimir Oltean */ 825d0004a02SVladimir Oltean dsa_switch_for_each_user_port(dp, ds) { 826d0004a02SVladimir Oltean struct net_device *slave = dp->slave; 82753da0ebaSVladimir Oltean 82853da0ebaSVladimir Oltean dsa_slave_setup_tagger(slave); 82953da0ebaSVladimir Oltean 83053da0ebaSVladimir Oltean /* rtnl_mutex is held in dsa_tree_change_tag_proto */ 83153da0ebaSVladimir Oltean dsa_slave_change_mtu(slave, slave->mtu); 83253da0ebaSVladimir Oltean } 83353da0ebaSVladimir Oltean 83453da0ebaSVladimir Oltean return 0; 83553da0ebaSVladimir Oltean } 83653da0ebaSVladimir Oltean 8377f297314SVladimir Oltean /* We use the same cross-chip notifiers to inform both the tagger side, as well 8387f297314SVladimir Oltean * as the switch side, of connection and disconnection events. 8397f297314SVladimir Oltean * Since ds->tagger_data is owned by the tagger, it isn't a hard error if the 8407f297314SVladimir Oltean * switch side doesn't support connecting to this tagger, and therefore, the 8417f297314SVladimir Oltean * fact that we don't disconnect the tagger side doesn't constitute a memory 8427f297314SVladimir Oltean * leak: the tagger will still operate with persistent per-switch memory, just 8437f297314SVladimir Oltean * with the switch side unconnected to it. What does constitute a hard error is 8447f297314SVladimir Oltean * when the switch side supports connecting but fails. 8457f297314SVladimir Oltean */ 8467f297314SVladimir Oltean static int 8477f297314SVladimir Oltean dsa_switch_connect_tag_proto(struct dsa_switch *ds, 848dc452a47SVladimir Oltean struct dsa_notifier_tag_proto_info *info) 849dc452a47SVladimir Oltean { 850dc452a47SVladimir Oltean const struct dsa_device_ops *tag_ops = info->tag_ops; 8517f297314SVladimir Oltean int err; 8527f297314SVladimir Oltean 8537f297314SVladimir Oltean /* Notify the new tagger about the connection to this switch */ 8547f297314SVladimir Oltean if (tag_ops->connect) { 8557f297314SVladimir Oltean err = tag_ops->connect(ds); 8567f297314SVladimir Oltean if (err) 8577f297314SVladimir Oltean return err; 8587f297314SVladimir Oltean } 859dc452a47SVladimir Oltean 860dc452a47SVladimir Oltean if (!ds->ops->connect_tag_protocol) 861dc452a47SVladimir Oltean return -EOPNOTSUPP; 862dc452a47SVladimir Oltean 8637f297314SVladimir Oltean /* Notify the switch about the connection to the new tagger */ 8647f297314SVladimir Oltean err = ds->ops->connect_tag_protocol(ds, tag_ops->proto); 8657f297314SVladimir Oltean if (err) { 8667f297314SVladimir Oltean /* Revert the new tagger's connection to this tree */ 8677f297314SVladimir Oltean if (tag_ops->disconnect) 8687f297314SVladimir Oltean tag_ops->disconnect(ds); 8697f297314SVladimir Oltean return err; 8707f297314SVladimir Oltean } 8717f297314SVladimir Oltean 8727f297314SVladimir Oltean return 0; 8737f297314SVladimir Oltean } 8747f297314SVladimir Oltean 8757f297314SVladimir Oltean static int 8767f297314SVladimir Oltean dsa_switch_disconnect_tag_proto(struct dsa_switch *ds, 8777f297314SVladimir Oltean struct dsa_notifier_tag_proto_info *info) 8787f297314SVladimir Oltean { 8797f297314SVladimir Oltean const struct dsa_device_ops *tag_ops = info->tag_ops; 8807f297314SVladimir Oltean 8817f297314SVladimir Oltean /* Notify the tagger about the disconnection from this switch */ 8827f297314SVladimir Oltean if (tag_ops->disconnect && ds->tagger_data) 8837f297314SVladimir Oltean tag_ops->disconnect(ds); 8847f297314SVladimir Oltean 8857f297314SVladimir Oltean /* No need to notify the switch, since it shouldn't have any 8867f297314SVladimir Oltean * resources to tear down 8877f297314SVladimir Oltean */ 8887f297314SVladimir Oltean return 0; 889dc452a47SVladimir Oltean } 890dc452a47SVladimir Oltean 891295ab96fSVladimir Oltean static int 892295ab96fSVladimir Oltean dsa_switch_master_state_change(struct dsa_switch *ds, 893295ab96fSVladimir Oltean struct dsa_notifier_master_state_info *info) 894295ab96fSVladimir Oltean { 895295ab96fSVladimir Oltean if (!ds->ops->master_state_change) 896295ab96fSVladimir Oltean return 0; 897295ab96fSVladimir Oltean 898295ab96fSVladimir Oltean ds->ops->master_state_change(ds, info->master, info->operational); 899295ab96fSVladimir Oltean 900295ab96fSVladimir Oltean return 0; 901295ab96fSVladimir Oltean } 902295ab96fSVladimir Oltean 903f515f192SVivien Didelot static int dsa_switch_event(struct notifier_block *nb, 904f515f192SVivien Didelot unsigned long event, void *info) 905f515f192SVivien Didelot { 906f515f192SVivien Didelot struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb); 907f515f192SVivien Didelot int err; 908f515f192SVivien Didelot 909f515f192SVivien Didelot switch (event) { 9101faabf74SVivien Didelot case DSA_NOTIFIER_AGEING_TIME: 9111faabf74SVivien Didelot err = dsa_switch_ageing_time(ds, info); 9121faabf74SVivien Didelot break; 91304d3a4c6SVivien Didelot case DSA_NOTIFIER_BRIDGE_JOIN: 91404d3a4c6SVivien Didelot err = dsa_switch_bridge_join(ds, info); 91504d3a4c6SVivien Didelot break; 91604d3a4c6SVivien Didelot case DSA_NOTIFIER_BRIDGE_LEAVE: 91704d3a4c6SVivien Didelot err = dsa_switch_bridge_leave(ds, info); 91804d3a4c6SVivien Didelot break; 919685fb6a4SVivien Didelot case DSA_NOTIFIER_FDB_ADD: 920685fb6a4SVivien Didelot err = dsa_switch_fdb_add(ds, info); 921685fb6a4SVivien Didelot break; 922685fb6a4SVivien Didelot case DSA_NOTIFIER_FDB_DEL: 923685fb6a4SVivien Didelot err = dsa_switch_fdb_del(ds, info); 924685fb6a4SVivien Didelot break; 9253dc80afcSVladimir Oltean case DSA_NOTIFIER_HOST_FDB_ADD: 9263dc80afcSVladimir Oltean err = dsa_switch_host_fdb_add(ds, info); 9273dc80afcSVladimir Oltean break; 9283dc80afcSVladimir Oltean case DSA_NOTIFIER_HOST_FDB_DEL: 9293dc80afcSVladimir Oltean err = dsa_switch_host_fdb_del(ds, info); 9303dc80afcSVladimir Oltean break; 931e212fa7cSVladimir Oltean case DSA_NOTIFIER_LAG_FDB_ADD: 932e212fa7cSVladimir Oltean err = dsa_switch_lag_fdb_add(ds, info); 933e212fa7cSVladimir Oltean break; 934e212fa7cSVladimir Oltean case DSA_NOTIFIER_LAG_FDB_DEL: 935e212fa7cSVladimir Oltean err = dsa_switch_lag_fdb_del(ds, info); 936e212fa7cSVladimir Oltean break; 937058102a6STobias Waldekranz case DSA_NOTIFIER_LAG_CHANGE: 938058102a6STobias Waldekranz err = dsa_switch_lag_change(ds, info); 939058102a6STobias Waldekranz break; 940058102a6STobias Waldekranz case DSA_NOTIFIER_LAG_JOIN: 941058102a6STobias Waldekranz err = dsa_switch_lag_join(ds, info); 942058102a6STobias Waldekranz break; 943058102a6STobias Waldekranz case DSA_NOTIFIER_LAG_LEAVE: 944058102a6STobias Waldekranz err = dsa_switch_lag_leave(ds, info); 945058102a6STobias Waldekranz break; 9468ae5bcdcSVivien Didelot case DSA_NOTIFIER_MDB_ADD: 9478ae5bcdcSVivien Didelot err = dsa_switch_mdb_add(ds, info); 9488ae5bcdcSVivien Didelot break; 9498ae5bcdcSVivien Didelot case DSA_NOTIFIER_MDB_DEL: 9508ae5bcdcSVivien Didelot err = dsa_switch_mdb_del(ds, info); 9518ae5bcdcSVivien Didelot break; 952b8e997c4SVladimir Oltean case DSA_NOTIFIER_HOST_MDB_ADD: 953b8e997c4SVladimir Oltean err = dsa_switch_host_mdb_add(ds, info); 954b8e997c4SVladimir Oltean break; 955b8e997c4SVladimir Oltean case DSA_NOTIFIER_HOST_MDB_DEL: 956b8e997c4SVladimir Oltean err = dsa_switch_host_mdb_del(ds, info); 957b8e997c4SVladimir Oltean break; 958d0c627b8SVivien Didelot case DSA_NOTIFIER_VLAN_ADD: 959d0c627b8SVivien Didelot err = dsa_switch_vlan_add(ds, info); 960d0c627b8SVivien Didelot break; 961d0c627b8SVivien Didelot case DSA_NOTIFIER_VLAN_DEL: 962d0c627b8SVivien Didelot err = dsa_switch_vlan_del(ds, info); 963d0c627b8SVivien Didelot break; 964134ef238SVladimir Oltean case DSA_NOTIFIER_HOST_VLAN_ADD: 965134ef238SVladimir Oltean err = dsa_switch_host_vlan_add(ds, info); 966134ef238SVladimir Oltean break; 967134ef238SVladimir Oltean case DSA_NOTIFIER_HOST_VLAN_DEL: 968134ef238SVladimir Oltean err = dsa_switch_host_vlan_del(ds, info); 969134ef238SVladimir Oltean break; 970bfcb8132SVladimir Oltean case DSA_NOTIFIER_MTU: 971bfcb8132SVladimir Oltean err = dsa_switch_mtu(ds, info); 972bfcb8132SVladimir Oltean break; 97353da0ebaSVladimir Oltean case DSA_NOTIFIER_TAG_PROTO: 97453da0ebaSVladimir Oltean err = dsa_switch_change_tag_proto(ds, info); 97553da0ebaSVladimir Oltean break; 976dc452a47SVladimir Oltean case DSA_NOTIFIER_TAG_PROTO_CONNECT: 977dc452a47SVladimir Oltean err = dsa_switch_connect_tag_proto(ds, info); 978dc452a47SVladimir Oltean break; 9797f297314SVladimir Oltean case DSA_NOTIFIER_TAG_PROTO_DISCONNECT: 9807f297314SVladimir Oltean err = dsa_switch_disconnect_tag_proto(ds, info); 9817f297314SVladimir Oltean break; 982c64b9c05SVladimir Oltean case DSA_NOTIFIER_TAG_8021Q_VLAN_ADD: 983c64b9c05SVladimir Oltean err = dsa_switch_tag_8021q_vlan_add(ds, info); 984c64b9c05SVladimir Oltean break; 985c64b9c05SVladimir Oltean case DSA_NOTIFIER_TAG_8021Q_VLAN_DEL: 986c64b9c05SVladimir Oltean err = dsa_switch_tag_8021q_vlan_del(ds, info); 987c64b9c05SVladimir Oltean break; 988295ab96fSVladimir Oltean case DSA_NOTIFIER_MASTER_STATE_CHANGE: 989295ab96fSVladimir Oltean err = dsa_switch_master_state_change(ds, info); 990295ab96fSVladimir Oltean break; 991f515f192SVivien Didelot default: 992f515f192SVivien Didelot err = -EOPNOTSUPP; 993f515f192SVivien Didelot break; 994f515f192SVivien Didelot } 995f515f192SVivien Didelot 996f515f192SVivien Didelot if (err) 997f515f192SVivien Didelot dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n", 998f515f192SVivien Didelot event, err); 999f515f192SVivien Didelot 1000f515f192SVivien Didelot return notifier_from_errno(err); 1001f515f192SVivien Didelot } 1002f515f192SVivien Didelot 1003f515f192SVivien Didelot int dsa_switch_register_notifier(struct dsa_switch *ds) 1004f515f192SVivien Didelot { 1005f515f192SVivien Didelot ds->nb.notifier_call = dsa_switch_event; 1006f515f192SVivien Didelot 1007f515f192SVivien Didelot return raw_notifier_chain_register(&ds->dst->nh, &ds->nb); 1008f515f192SVivien Didelot } 1009f515f192SVivien Didelot 1010f515f192SVivien Didelot void dsa_switch_unregister_notifier(struct dsa_switch *ds) 1011f515f192SVivien Didelot { 1012f515f192SVivien Didelot int err; 1013f515f192SVivien Didelot 1014f515f192SVivien Didelot err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb); 1015f515f192SVivien Didelot if (err) 1016f515f192SVivien Didelot dev_err(ds->dev, "failed to unregister notifier (%d)\n", err); 1017f515f192SVivien Didelot } 1018