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" 16022bba63SVladimir Oltean #include "port.h" 1709f92341SVladimir Oltean #include "slave.h" 18*0c603136SVladimir Oltean #include "switch.h" 19f515f192SVivien Didelot 201faabf74SVivien Didelot static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds, 211faabf74SVivien Didelot unsigned int ageing_time) 221faabf74SVivien Didelot { 23d0004a02SVladimir Oltean struct dsa_port *dp; 241faabf74SVivien Didelot 25d0004a02SVladimir Oltean dsa_switch_for_each_port(dp, ds) 261faabf74SVivien Didelot if (dp->ageing_time && dp->ageing_time < ageing_time) 271faabf74SVivien Didelot ageing_time = dp->ageing_time; 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 52fac6abd5SVladimir Oltean static bool dsa_port_mtu_match(struct dsa_port *dp, 53bfcb8132SVladimir Oltean struct dsa_notifier_mtu_info *info) 54bfcb8132SVladimir Oltean { 55be6ff966SVladimir Oltean return dp == info->dp || dsa_port_is_dsa(dp) || dsa_port_is_cpu(dp); 56bfcb8132SVladimir Oltean } 57bfcb8132SVladimir Oltean 58bfcb8132SVladimir Oltean static int dsa_switch_mtu(struct dsa_switch *ds, 59bfcb8132SVladimir Oltean struct dsa_notifier_mtu_info *info) 60bfcb8132SVladimir Oltean { 61fac6abd5SVladimir Oltean struct dsa_port *dp; 62fac6abd5SVladimir Oltean int ret; 63bfcb8132SVladimir Oltean 64bfcb8132SVladimir Oltean if (!ds->ops->port_change_mtu) 65bfcb8132SVladimir Oltean return -EOPNOTSUPP; 66bfcb8132SVladimir Oltean 67fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 68fac6abd5SVladimir Oltean if (dsa_port_mtu_match(dp, info)) { 69fac6abd5SVladimir Oltean ret = ds->ops->port_change_mtu(ds, dp->index, 70fac6abd5SVladimir Oltean info->mtu); 71bfcb8132SVladimir Oltean if (ret) 72bfcb8132SVladimir Oltean return ret; 73bfcb8132SVladimir Oltean } 74bfcb8132SVladimir Oltean } 75bfcb8132SVladimir Oltean 76bfcb8132SVladimir Oltean return 0; 77bfcb8132SVladimir Oltean } 78bfcb8132SVladimir Oltean 7904d3a4c6SVivien Didelot static int dsa_switch_bridge_join(struct dsa_switch *ds, 8004d3a4c6SVivien Didelot struct dsa_notifier_bridge_info *info) 8104d3a4c6SVivien Didelot { 82e19cc13cSVladimir Oltean int err; 83f66a6a69SVladimir Oltean 84726816a1SVladimir Oltean if (info->dp->ds == ds) { 8567b5fb5dSVladimir Oltean if (!ds->ops->port_bridge_join) 8667b5fb5dSVladimir Oltean return -EOPNOTSUPP; 8767b5fb5dSVladimir Oltean 88726816a1SVladimir Oltean err = ds->ops->port_bridge_join(ds, info->dp->index, 89726816a1SVladimir Oltean info->bridge, 9006b9cce4SVladimir Oltean &info->tx_fwd_offload, 9106b9cce4SVladimir Oltean info->extack); 92e19cc13cSVladimir Oltean if (err) 93e19cc13cSVladimir Oltean return err; 94e19cc13cSVladimir Oltean } 9504d3a4c6SVivien Didelot 96726816a1SVladimir Oltean if (info->dp->ds != ds && ds->ops->crosschip_bridge_join) { 97726816a1SVladimir Oltean err = ds->ops->crosschip_bridge_join(ds, 98726816a1SVladimir Oltean info->dp->ds->dst->index, 99726816a1SVladimir Oltean info->dp->ds->index, 100726816a1SVladimir Oltean info->dp->index, 101726816a1SVladimir Oltean info->bridge, 10206b9cce4SVladimir Oltean info->extack); 103e19cc13cSVladimir Oltean if (err) 104e19cc13cSVladimir Oltean return err; 105e19cc13cSVladimir Oltean } 10604d3a4c6SVivien Didelot 10791495f21SVladimir Oltean return 0; 10804d3a4c6SVivien Didelot } 10904d3a4c6SVivien Didelot 110381a7301STobias Waldekranz static int dsa_switch_bridge_leave(struct dsa_switch *ds, 111381a7301STobias Waldekranz struct dsa_notifier_bridge_info *info) 112381a7301STobias Waldekranz { 113726816a1SVladimir Oltean if (info->dp->ds == ds && ds->ops->port_bridge_leave) 114726816a1SVladimir Oltean ds->ops->port_bridge_leave(ds, info->dp->index, info->bridge); 115381a7301STobias Waldekranz 116726816a1SVladimir Oltean if (info->dp->ds != ds && ds->ops->crosschip_bridge_leave) 117726816a1SVladimir Oltean ds->ops->crosschip_bridge_leave(ds, info->dp->ds->dst->index, 118726816a1SVladimir Oltean info->dp->ds->index, 119726816a1SVladimir Oltean info->dp->index, 120381a7301STobias Waldekranz info->bridge); 121381a7301STobias Waldekranz 12291495f21SVladimir Oltean return 0; 12304d3a4c6SVivien Didelot } 12404d3a4c6SVivien Didelot 125b8e997c4SVladimir Oltean /* Matches for all upstream-facing ports (the CPU port and all upstream-facing 126b8e997c4SVladimir Oltean * DSA links) that sit between the targeted port on which the notifier was 127b8e997c4SVladimir Oltean * emitted and its dedicated CPU port. 128b8e997c4SVladimir Oltean */ 129fac6abd5SVladimir Oltean static bool dsa_port_host_address_match(struct dsa_port *dp, 130726816a1SVladimir Oltean const struct dsa_port *targeted_dp) 131b8e997c4SVladimir Oltean { 132726816a1SVladimir Oltean struct dsa_port *cpu_dp = targeted_dp->cpu_dp; 133b8e997c4SVladimir Oltean 134726816a1SVladimir Oltean if (dsa_switch_is_upstream_of(dp->ds, targeted_dp->ds)) 135fac6abd5SVladimir Oltean return dp->index == dsa_towards_port(dp->ds, cpu_dp->ds->index, 136b8e997c4SVladimir Oltean cpu_dp->index); 137b8e997c4SVladimir Oltean 138b8e997c4SVladimir Oltean return false; 139b8e997c4SVladimir Oltean } 140b8e997c4SVladimir Oltean 141161ca59dSVladimir Oltean static struct dsa_mac_addr *dsa_mac_addr_find(struct list_head *addr_list, 142c2693363SVladimir Oltean const unsigned char *addr, u16 vid, 143c2693363SVladimir Oltean struct dsa_db db) 144161ca59dSVladimir Oltean { 145161ca59dSVladimir Oltean struct dsa_mac_addr *a; 146161ca59dSVladimir Oltean 147161ca59dSVladimir Oltean list_for_each_entry(a, addr_list, list) 148c2693363SVladimir Oltean if (ether_addr_equal(a->addr, addr) && a->vid == vid && 149c2693363SVladimir Oltean dsa_db_equal(&a->db, &db)) 150161ca59dSVladimir Oltean return a; 151161ca59dSVladimir Oltean 152161ca59dSVladimir Oltean return NULL; 153161ca59dSVladimir Oltean } 154161ca59dSVladimir Oltean 155fac6abd5SVladimir Oltean static int dsa_port_do_mdb_add(struct dsa_port *dp, 156c2693363SVladimir Oltean const struct switchdev_obj_port_mdb *mdb, 157c2693363SVladimir Oltean struct dsa_db db) 158161ca59dSVladimir Oltean { 159fac6abd5SVladimir Oltean struct dsa_switch *ds = dp->ds; 160161ca59dSVladimir Oltean struct dsa_mac_addr *a; 161fac6abd5SVladimir Oltean int port = dp->index; 162338a3a47SVladimir Oltean int err = 0; 163161ca59dSVladimir Oltean 164161ca59dSVladimir Oltean /* No need to bother with refcounting for user ports */ 165161ca59dSVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 166c2693363SVladimir Oltean return ds->ops->port_mdb_add(ds, port, mdb, db); 167161ca59dSVladimir Oltean 168338a3a47SVladimir Oltean mutex_lock(&dp->addr_lists_lock); 169338a3a47SVladimir Oltean 170c2693363SVladimir Oltean a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid, db); 171161ca59dSVladimir Oltean if (a) { 172161ca59dSVladimir Oltean refcount_inc(&a->refcount); 173338a3a47SVladimir Oltean goto out; 174161ca59dSVladimir Oltean } 175161ca59dSVladimir Oltean 176161ca59dSVladimir Oltean a = kzalloc(sizeof(*a), GFP_KERNEL); 177338a3a47SVladimir Oltean if (!a) { 178338a3a47SVladimir Oltean err = -ENOMEM; 179338a3a47SVladimir Oltean goto out; 180338a3a47SVladimir Oltean } 181161ca59dSVladimir Oltean 182c2693363SVladimir Oltean err = ds->ops->port_mdb_add(ds, port, mdb, db); 183161ca59dSVladimir Oltean if (err) { 184161ca59dSVladimir Oltean kfree(a); 185338a3a47SVladimir Oltean goto out; 186161ca59dSVladimir Oltean } 187161ca59dSVladimir Oltean 188161ca59dSVladimir Oltean ether_addr_copy(a->addr, mdb->addr); 189161ca59dSVladimir Oltean a->vid = mdb->vid; 190c2693363SVladimir Oltean a->db = db; 191161ca59dSVladimir Oltean refcount_set(&a->refcount, 1); 192161ca59dSVladimir Oltean list_add_tail(&a->list, &dp->mdbs); 193161ca59dSVladimir Oltean 194338a3a47SVladimir Oltean out: 195338a3a47SVladimir Oltean mutex_unlock(&dp->addr_lists_lock); 196338a3a47SVladimir Oltean 197338a3a47SVladimir Oltean return err; 198161ca59dSVladimir Oltean } 199161ca59dSVladimir Oltean 200fac6abd5SVladimir Oltean static int dsa_port_do_mdb_del(struct dsa_port *dp, 201c2693363SVladimir Oltean const struct switchdev_obj_port_mdb *mdb, 202c2693363SVladimir Oltean struct dsa_db db) 203161ca59dSVladimir Oltean { 204fac6abd5SVladimir Oltean struct dsa_switch *ds = dp->ds; 205161ca59dSVladimir Oltean struct dsa_mac_addr *a; 206fac6abd5SVladimir Oltean int port = dp->index; 207338a3a47SVladimir Oltean int err = 0; 208161ca59dSVladimir Oltean 209161ca59dSVladimir Oltean /* No need to bother with refcounting for user ports */ 210161ca59dSVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 211c2693363SVladimir Oltean return ds->ops->port_mdb_del(ds, port, mdb, db); 212161ca59dSVladimir Oltean 213338a3a47SVladimir Oltean mutex_lock(&dp->addr_lists_lock); 214338a3a47SVladimir Oltean 215c2693363SVladimir Oltean a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid, db); 216338a3a47SVladimir Oltean if (!a) { 217338a3a47SVladimir Oltean err = -ENOENT; 218338a3a47SVladimir Oltean goto out; 219338a3a47SVladimir Oltean } 220161ca59dSVladimir Oltean 221161ca59dSVladimir Oltean if (!refcount_dec_and_test(&a->refcount)) 222338a3a47SVladimir Oltean goto out; 223161ca59dSVladimir Oltean 224c2693363SVladimir Oltean err = ds->ops->port_mdb_del(ds, port, mdb, db); 225161ca59dSVladimir Oltean if (err) { 226232deb3fSVladimir Oltean refcount_set(&a->refcount, 1); 227338a3a47SVladimir Oltean goto out; 228161ca59dSVladimir Oltean } 229161ca59dSVladimir Oltean 230161ca59dSVladimir Oltean list_del(&a->list); 231161ca59dSVladimir Oltean kfree(a); 232161ca59dSVladimir Oltean 233338a3a47SVladimir Oltean out: 234338a3a47SVladimir Oltean mutex_unlock(&dp->addr_lists_lock); 235338a3a47SVladimir Oltean 236338a3a47SVladimir Oltean return err; 237161ca59dSVladimir Oltean } 238161ca59dSVladimir Oltean 239fac6abd5SVladimir Oltean static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr, 240c2693363SVladimir Oltean u16 vid, struct dsa_db db) 2413f6e32f9SVladimir Oltean { 242fac6abd5SVladimir Oltean struct dsa_switch *ds = dp->ds; 2433f6e32f9SVladimir Oltean struct dsa_mac_addr *a; 244fac6abd5SVladimir Oltean int port = dp->index; 245338a3a47SVladimir Oltean int err = 0; 2463f6e32f9SVladimir Oltean 2473f6e32f9SVladimir Oltean /* No need to bother with refcounting for user ports */ 2483f6e32f9SVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 249c2693363SVladimir Oltean return ds->ops->port_fdb_add(ds, port, addr, vid, db); 2503f6e32f9SVladimir Oltean 251338a3a47SVladimir Oltean mutex_lock(&dp->addr_lists_lock); 252338a3a47SVladimir Oltean 253c2693363SVladimir Oltean a = dsa_mac_addr_find(&dp->fdbs, addr, vid, db); 2543f6e32f9SVladimir Oltean if (a) { 2553f6e32f9SVladimir Oltean refcount_inc(&a->refcount); 256338a3a47SVladimir Oltean goto out; 2573f6e32f9SVladimir Oltean } 2583f6e32f9SVladimir Oltean 2593f6e32f9SVladimir Oltean a = kzalloc(sizeof(*a), GFP_KERNEL); 260338a3a47SVladimir Oltean if (!a) { 261338a3a47SVladimir Oltean err = -ENOMEM; 262338a3a47SVladimir Oltean goto out; 263338a3a47SVladimir Oltean } 2643f6e32f9SVladimir Oltean 265c2693363SVladimir Oltean err = ds->ops->port_fdb_add(ds, port, addr, vid, db); 2663f6e32f9SVladimir Oltean if (err) { 2673f6e32f9SVladimir Oltean kfree(a); 268338a3a47SVladimir Oltean goto out; 2693f6e32f9SVladimir Oltean } 2703f6e32f9SVladimir Oltean 2713f6e32f9SVladimir Oltean ether_addr_copy(a->addr, addr); 2723f6e32f9SVladimir Oltean a->vid = vid; 273c2693363SVladimir Oltean a->db = db; 2743f6e32f9SVladimir Oltean refcount_set(&a->refcount, 1); 2753f6e32f9SVladimir Oltean list_add_tail(&a->list, &dp->fdbs); 2763f6e32f9SVladimir Oltean 277338a3a47SVladimir Oltean out: 278338a3a47SVladimir Oltean mutex_unlock(&dp->addr_lists_lock); 279338a3a47SVladimir Oltean 280338a3a47SVladimir Oltean return err; 2813f6e32f9SVladimir Oltean } 2823f6e32f9SVladimir Oltean 283fac6abd5SVladimir Oltean static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr, 284c2693363SVladimir Oltean u16 vid, struct dsa_db db) 2853f6e32f9SVladimir Oltean { 286fac6abd5SVladimir Oltean struct dsa_switch *ds = dp->ds; 2873f6e32f9SVladimir Oltean struct dsa_mac_addr *a; 288fac6abd5SVladimir Oltean int port = dp->index; 289338a3a47SVladimir Oltean int err = 0; 2903f6e32f9SVladimir Oltean 2913f6e32f9SVladimir Oltean /* No need to bother with refcounting for user ports */ 2923f6e32f9SVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 293c2693363SVladimir Oltean return ds->ops->port_fdb_del(ds, port, addr, vid, db); 2943f6e32f9SVladimir Oltean 295338a3a47SVladimir Oltean mutex_lock(&dp->addr_lists_lock); 296338a3a47SVladimir Oltean 297c2693363SVladimir Oltean a = dsa_mac_addr_find(&dp->fdbs, addr, vid, db); 298338a3a47SVladimir Oltean if (!a) { 299338a3a47SVladimir Oltean err = -ENOENT; 300338a3a47SVladimir Oltean goto out; 301338a3a47SVladimir Oltean } 3023f6e32f9SVladimir Oltean 3033f6e32f9SVladimir Oltean if (!refcount_dec_and_test(&a->refcount)) 304338a3a47SVladimir Oltean goto out; 3053f6e32f9SVladimir Oltean 306c2693363SVladimir Oltean err = ds->ops->port_fdb_del(ds, port, addr, vid, db); 3073f6e32f9SVladimir Oltean if (err) { 308232deb3fSVladimir Oltean refcount_set(&a->refcount, 1); 309338a3a47SVladimir Oltean goto out; 3103f6e32f9SVladimir Oltean } 3113f6e32f9SVladimir Oltean 3123f6e32f9SVladimir Oltean list_del(&a->list); 3133f6e32f9SVladimir Oltean kfree(a); 3143f6e32f9SVladimir Oltean 315338a3a47SVladimir Oltean out: 316338a3a47SVladimir Oltean mutex_unlock(&dp->addr_lists_lock); 317338a3a47SVladimir Oltean 318338a3a47SVladimir Oltean return err; 3193f6e32f9SVladimir Oltean } 3203f6e32f9SVladimir Oltean 321e212fa7cSVladimir Oltean static int dsa_switch_do_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag *lag, 322c2693363SVladimir Oltean const unsigned char *addr, u16 vid, 323c2693363SVladimir Oltean struct dsa_db db) 324e212fa7cSVladimir Oltean { 325e212fa7cSVladimir Oltean struct dsa_mac_addr *a; 326e212fa7cSVladimir Oltean int err = 0; 327e212fa7cSVladimir Oltean 328e212fa7cSVladimir Oltean mutex_lock(&lag->fdb_lock); 329e212fa7cSVladimir Oltean 330c2693363SVladimir Oltean a = dsa_mac_addr_find(&lag->fdbs, addr, vid, db); 331e212fa7cSVladimir Oltean if (a) { 332e212fa7cSVladimir Oltean refcount_inc(&a->refcount); 333e212fa7cSVladimir Oltean goto out; 334e212fa7cSVladimir Oltean } 335e212fa7cSVladimir Oltean 336e212fa7cSVladimir Oltean a = kzalloc(sizeof(*a), GFP_KERNEL); 337e212fa7cSVladimir Oltean if (!a) { 338e212fa7cSVladimir Oltean err = -ENOMEM; 339e212fa7cSVladimir Oltean goto out; 340e212fa7cSVladimir Oltean } 341e212fa7cSVladimir Oltean 342c2693363SVladimir Oltean err = ds->ops->lag_fdb_add(ds, *lag, addr, vid, db); 343e212fa7cSVladimir Oltean if (err) { 344e212fa7cSVladimir Oltean kfree(a); 345e212fa7cSVladimir Oltean goto out; 346e212fa7cSVladimir Oltean } 347e212fa7cSVladimir Oltean 348e212fa7cSVladimir Oltean ether_addr_copy(a->addr, addr); 349e212fa7cSVladimir Oltean a->vid = vid; 350c7560d12SVladimir Oltean a->db = db; 351e212fa7cSVladimir Oltean refcount_set(&a->refcount, 1); 352e212fa7cSVladimir Oltean list_add_tail(&a->list, &lag->fdbs); 353e212fa7cSVladimir Oltean 354e212fa7cSVladimir Oltean out: 355e212fa7cSVladimir Oltean mutex_unlock(&lag->fdb_lock); 356e212fa7cSVladimir Oltean 357e212fa7cSVladimir Oltean return err; 358e212fa7cSVladimir Oltean } 359e212fa7cSVladimir Oltean 360e212fa7cSVladimir Oltean static int dsa_switch_do_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag *lag, 361c2693363SVladimir Oltean const unsigned char *addr, u16 vid, 362c2693363SVladimir Oltean struct dsa_db db) 363e212fa7cSVladimir Oltean { 364e212fa7cSVladimir Oltean struct dsa_mac_addr *a; 365e212fa7cSVladimir Oltean int err = 0; 366e212fa7cSVladimir Oltean 367e212fa7cSVladimir Oltean mutex_lock(&lag->fdb_lock); 368e212fa7cSVladimir Oltean 369c2693363SVladimir Oltean a = dsa_mac_addr_find(&lag->fdbs, addr, vid, db); 370e212fa7cSVladimir Oltean if (!a) { 371e212fa7cSVladimir Oltean err = -ENOENT; 372e212fa7cSVladimir Oltean goto out; 373e212fa7cSVladimir Oltean } 374e212fa7cSVladimir Oltean 375e212fa7cSVladimir Oltean if (!refcount_dec_and_test(&a->refcount)) 376e212fa7cSVladimir Oltean goto out; 377e212fa7cSVladimir Oltean 378c2693363SVladimir Oltean err = ds->ops->lag_fdb_del(ds, *lag, addr, vid, db); 379e212fa7cSVladimir Oltean if (err) { 380e212fa7cSVladimir Oltean refcount_set(&a->refcount, 1); 381e212fa7cSVladimir Oltean goto out; 382e212fa7cSVladimir Oltean } 383e212fa7cSVladimir Oltean 384e212fa7cSVladimir Oltean list_del(&a->list); 385e212fa7cSVladimir Oltean kfree(a); 386e212fa7cSVladimir Oltean 387e212fa7cSVladimir Oltean out: 388e212fa7cSVladimir Oltean mutex_unlock(&lag->fdb_lock); 389e212fa7cSVladimir Oltean 390e212fa7cSVladimir Oltean return err; 391e212fa7cSVladimir Oltean } 392e212fa7cSVladimir Oltean 3933dc80afcSVladimir Oltean static int dsa_switch_host_fdb_add(struct dsa_switch *ds, 3943dc80afcSVladimir Oltean struct dsa_notifier_fdb_info *info) 3953dc80afcSVladimir Oltean { 396fac6abd5SVladimir Oltean struct dsa_port *dp; 3973dc80afcSVladimir Oltean int err = 0; 3983dc80afcSVladimir Oltean 3993dc80afcSVladimir Oltean if (!ds->ops->port_fdb_add) 4003dc80afcSVladimir Oltean return -EOPNOTSUPP; 4013dc80afcSVladimir Oltean 402fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 403726816a1SVladimir Oltean if (dsa_port_host_address_match(dp, info->dp)) { 404acc43b7bSVladimir Oltean if (dsa_port_is_cpu(dp) && info->dp->cpu_port_in_lag) { 405acc43b7bSVladimir Oltean err = dsa_switch_do_lag_fdb_add(ds, dp->lag, 406acc43b7bSVladimir Oltean info->addr, 407acc43b7bSVladimir Oltean info->vid, 408c2693363SVladimir Oltean info->db); 409acc43b7bSVladimir Oltean } else { 410acc43b7bSVladimir Oltean err = dsa_port_do_fdb_add(dp, info->addr, 411acc43b7bSVladimir Oltean info->vid, info->db); 412acc43b7bSVladimir Oltean } 4133dc80afcSVladimir Oltean if (err) 4143dc80afcSVladimir Oltean break; 4153dc80afcSVladimir Oltean } 4163dc80afcSVladimir Oltean } 4173dc80afcSVladimir Oltean 4183dc80afcSVladimir Oltean return err; 4193dc80afcSVladimir Oltean } 4203dc80afcSVladimir Oltean 4213dc80afcSVladimir Oltean static int dsa_switch_host_fdb_del(struct dsa_switch *ds, 4223dc80afcSVladimir Oltean struct dsa_notifier_fdb_info *info) 4233dc80afcSVladimir Oltean { 424fac6abd5SVladimir Oltean struct dsa_port *dp; 4253f6e32f9SVladimir Oltean int err = 0; 4263f6e32f9SVladimir Oltean 4273dc80afcSVladimir Oltean if (!ds->ops->port_fdb_del) 4283dc80afcSVladimir Oltean return -EOPNOTSUPP; 4293dc80afcSVladimir Oltean 430fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 431726816a1SVladimir Oltean if (dsa_port_host_address_match(dp, info->dp)) { 432acc43b7bSVladimir Oltean if (dsa_port_is_cpu(dp) && info->dp->cpu_port_in_lag) { 433acc43b7bSVladimir Oltean err = dsa_switch_do_lag_fdb_del(ds, dp->lag, 434acc43b7bSVladimir Oltean info->addr, 435acc43b7bSVladimir Oltean info->vid, 436c2693363SVladimir Oltean info->db); 437acc43b7bSVladimir Oltean } else { 438acc43b7bSVladimir Oltean err = dsa_port_do_fdb_del(dp, info->addr, 439acc43b7bSVladimir Oltean info->vid, info->db); 440acc43b7bSVladimir Oltean } 4413f6e32f9SVladimir Oltean if (err) 4423f6e32f9SVladimir Oltean break; 4433f6e32f9SVladimir Oltean } 4443f6e32f9SVladimir Oltean } 4453dc80afcSVladimir Oltean 4463f6e32f9SVladimir Oltean return err; 4473dc80afcSVladimir Oltean } 4483dc80afcSVladimir Oltean 449685fb6a4SVivien Didelot static int dsa_switch_fdb_add(struct dsa_switch *ds, 450685fb6a4SVivien Didelot struct dsa_notifier_fdb_info *info) 451685fb6a4SVivien Didelot { 452726816a1SVladimir Oltean int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index); 453fac6abd5SVladimir Oltean struct dsa_port *dp = dsa_to_port(ds, port); 454685fb6a4SVivien Didelot 4551b6dd556SArkadi Sharshevsky if (!ds->ops->port_fdb_add) 456685fb6a4SVivien Didelot return -EOPNOTSUPP; 457685fb6a4SVivien Didelot 458c2693363SVladimir Oltean return dsa_port_do_fdb_add(dp, info->addr, info->vid, info->db); 459685fb6a4SVivien Didelot } 460685fb6a4SVivien Didelot 461685fb6a4SVivien Didelot static int dsa_switch_fdb_del(struct dsa_switch *ds, 462685fb6a4SVivien Didelot struct dsa_notifier_fdb_info *info) 463685fb6a4SVivien Didelot { 464726816a1SVladimir Oltean int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index); 465fac6abd5SVladimir Oltean struct dsa_port *dp = dsa_to_port(ds, port); 466685fb6a4SVivien Didelot 467685fb6a4SVivien Didelot if (!ds->ops->port_fdb_del) 468685fb6a4SVivien Didelot return -EOPNOTSUPP; 469685fb6a4SVivien Didelot 470c2693363SVladimir Oltean return dsa_port_do_fdb_del(dp, info->addr, info->vid, info->db); 471685fb6a4SVivien Didelot } 472685fb6a4SVivien Didelot 473e212fa7cSVladimir Oltean static int dsa_switch_lag_fdb_add(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_add) 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_add(ds, info->lag, 485c2693363SVladimir Oltean info->addr, info->vid, 486c2693363SVladimir Oltean info->db); 487e212fa7cSVladimir Oltean 488e212fa7cSVladimir Oltean return 0; 489e212fa7cSVladimir Oltean } 490e212fa7cSVladimir Oltean 491e212fa7cSVladimir Oltean static int dsa_switch_lag_fdb_del(struct dsa_switch *ds, 492e212fa7cSVladimir Oltean struct dsa_notifier_lag_fdb_info *info) 493e212fa7cSVladimir Oltean { 494e212fa7cSVladimir Oltean struct dsa_port *dp; 495e212fa7cSVladimir Oltean 496e212fa7cSVladimir Oltean if (!ds->ops->lag_fdb_del) 497e212fa7cSVladimir Oltean return -EOPNOTSUPP; 498e212fa7cSVladimir Oltean 499e212fa7cSVladimir Oltean /* Notify switch only if it has a port in this LAG */ 500e212fa7cSVladimir Oltean dsa_switch_for_each_port(dp, ds) 501e212fa7cSVladimir Oltean if (dsa_port_offloads_lag(dp, info->lag)) 502e212fa7cSVladimir Oltean return dsa_switch_do_lag_fdb_del(ds, info->lag, 503c2693363SVladimir Oltean info->addr, info->vid, 504c2693363SVladimir Oltean info->db); 505e212fa7cSVladimir Oltean 506e212fa7cSVladimir Oltean return 0; 507e212fa7cSVladimir Oltean } 508e212fa7cSVladimir Oltean 509058102a6STobias Waldekranz static int dsa_switch_lag_change(struct dsa_switch *ds, 510058102a6STobias Waldekranz struct dsa_notifier_lag_info *info) 511058102a6STobias Waldekranz { 512726816a1SVladimir Oltean if (info->dp->ds == ds && ds->ops->port_lag_change) 513726816a1SVladimir Oltean return ds->ops->port_lag_change(ds, info->dp->index); 514058102a6STobias Waldekranz 515726816a1SVladimir Oltean if (info->dp->ds != ds && ds->ops->crosschip_lag_change) 516726816a1SVladimir Oltean return ds->ops->crosschip_lag_change(ds, info->dp->ds->index, 517726816a1SVladimir Oltean info->dp->index); 518058102a6STobias Waldekranz 519058102a6STobias Waldekranz return 0; 520058102a6STobias Waldekranz } 521058102a6STobias Waldekranz 522058102a6STobias Waldekranz static int dsa_switch_lag_join(struct dsa_switch *ds, 523058102a6STobias Waldekranz struct dsa_notifier_lag_info *info) 524058102a6STobias Waldekranz { 525726816a1SVladimir Oltean if (info->dp->ds == ds && ds->ops->port_lag_join) 526726816a1SVladimir Oltean return ds->ops->port_lag_join(ds, info->dp->index, info->lag, 5272e359b00SVladimir Oltean info->info, info->extack); 528058102a6STobias Waldekranz 529726816a1SVladimir Oltean if (info->dp->ds != ds && ds->ops->crosschip_lag_join) 530726816a1SVladimir Oltean return ds->ops->crosschip_lag_join(ds, info->dp->ds->index, 531726816a1SVladimir Oltean info->dp->index, info->lag, 5322e359b00SVladimir Oltean info->info, info->extack); 533058102a6STobias Waldekranz 534b71d0987SVladimir Oltean return -EOPNOTSUPP; 535058102a6STobias Waldekranz } 536058102a6STobias Waldekranz 537058102a6STobias Waldekranz static int dsa_switch_lag_leave(struct dsa_switch *ds, 538058102a6STobias Waldekranz struct dsa_notifier_lag_info *info) 539058102a6STobias Waldekranz { 540726816a1SVladimir Oltean if (info->dp->ds == ds && ds->ops->port_lag_leave) 541726816a1SVladimir Oltean return ds->ops->port_lag_leave(ds, info->dp->index, info->lag); 542058102a6STobias Waldekranz 543726816a1SVladimir Oltean if (info->dp->ds != ds && ds->ops->crosschip_lag_leave) 544726816a1SVladimir Oltean return ds->ops->crosschip_lag_leave(ds, info->dp->ds->index, 545726816a1SVladimir Oltean info->dp->index, info->lag); 546058102a6STobias Waldekranz 547b71d0987SVladimir Oltean return -EOPNOTSUPP; 548058102a6STobias Waldekranz } 549058102a6STobias Waldekranz 550ffb68fc5SVladimir Oltean static int dsa_switch_mdb_add(struct dsa_switch *ds, 551e65d45ccSVivien Didelot struct dsa_notifier_mdb_info *info) 552e6db98dbSVivien Didelot { 553726816a1SVladimir Oltean int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index); 554fac6abd5SVladimir Oltean struct dsa_port *dp = dsa_to_port(ds, port); 555e6db98dbSVivien Didelot 556a52b2da7SVladimir Oltean if (!ds->ops->port_mdb_add) 557e6db98dbSVivien Didelot return -EOPNOTSUPP; 558e6db98dbSVivien Didelot 559c2693363SVladimir Oltean return dsa_port_do_mdb_add(dp, info->mdb, info->db); 560e6db98dbSVivien Didelot } 5618ae5bcdcSVivien Didelot 5628ae5bcdcSVivien Didelot static int dsa_switch_mdb_del(struct dsa_switch *ds, 5638ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info *info) 5648ae5bcdcSVivien Didelot { 565726816a1SVladimir Oltean int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index); 566fac6abd5SVladimir Oltean struct dsa_port *dp = dsa_to_port(ds, port); 567161ca59dSVladimir Oltean 5688ae5bcdcSVivien Didelot if (!ds->ops->port_mdb_del) 5698ae5bcdcSVivien Didelot return -EOPNOTSUPP; 5708ae5bcdcSVivien Didelot 571c2693363SVladimir Oltean return dsa_port_do_mdb_del(dp, info->mdb, info->db); 5728ae5bcdcSVivien Didelot } 5738ae5bcdcSVivien Didelot 574b8e997c4SVladimir Oltean static int dsa_switch_host_mdb_add(struct dsa_switch *ds, 575b8e997c4SVladimir Oltean struct dsa_notifier_mdb_info *info) 576b8e997c4SVladimir Oltean { 577fac6abd5SVladimir Oltean struct dsa_port *dp; 578b8e997c4SVladimir Oltean int err = 0; 579b8e997c4SVladimir Oltean 580b8e997c4SVladimir Oltean if (!ds->ops->port_mdb_add) 581b8e997c4SVladimir Oltean return -EOPNOTSUPP; 582b8e997c4SVladimir Oltean 583fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 584726816a1SVladimir Oltean if (dsa_port_host_address_match(dp, info->dp)) { 585c2693363SVladimir Oltean err = dsa_port_do_mdb_add(dp, info->mdb, info->db); 586b8e997c4SVladimir Oltean if (err) 587b8e997c4SVladimir Oltean break; 588b8e997c4SVladimir Oltean } 589b8e997c4SVladimir Oltean } 590b8e997c4SVladimir Oltean 591b8e997c4SVladimir Oltean return err; 592b8e997c4SVladimir Oltean } 593b8e997c4SVladimir Oltean 594b8e997c4SVladimir Oltean static int dsa_switch_host_mdb_del(struct dsa_switch *ds, 595b8e997c4SVladimir Oltean struct dsa_notifier_mdb_info *info) 596b8e997c4SVladimir Oltean { 597fac6abd5SVladimir Oltean struct dsa_port *dp; 598161ca59dSVladimir Oltean int err = 0; 599161ca59dSVladimir Oltean 600b8e997c4SVladimir Oltean if (!ds->ops->port_mdb_del) 601b8e997c4SVladimir Oltean return -EOPNOTSUPP; 602b8e997c4SVladimir Oltean 603fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 604726816a1SVladimir Oltean if (dsa_port_host_address_match(dp, info->dp)) { 605c2693363SVladimir Oltean err = dsa_port_do_mdb_del(dp, info->mdb, info->db); 606161ca59dSVladimir Oltean if (err) 607161ca59dSVladimir Oltean break; 608161ca59dSVladimir Oltean } 609161ca59dSVladimir Oltean } 610b8e997c4SVladimir Oltean 611161ca59dSVladimir Oltean return err; 612b8e997c4SVladimir Oltean } 613b8e997c4SVladimir Oltean 614134ef238SVladimir Oltean /* Port VLANs match on the targeted port and on all DSA ports */ 615fac6abd5SVladimir Oltean static bool dsa_port_vlan_match(struct dsa_port *dp, 616e65d45ccSVivien Didelot struct dsa_notifier_vlan_info *info) 617e65d45ccSVivien Didelot { 618726816a1SVladimir Oltean return dsa_port_is_dsa(dp) || dp == info->dp; 619e65d45ccSVivien Didelot } 620e65d45ccSVivien Didelot 621134ef238SVladimir Oltean /* Host VLANs match on the targeted port's CPU port, and on all DSA ports 622134ef238SVladimir Oltean * (upstream and downstream) of that switch and its upstream switches. 623134ef238SVladimir Oltean */ 624134ef238SVladimir Oltean static bool dsa_port_host_vlan_match(struct dsa_port *dp, 625726816a1SVladimir Oltean const struct dsa_port *targeted_dp) 626134ef238SVladimir Oltean { 627726816a1SVladimir Oltean struct dsa_port *cpu_dp = targeted_dp->cpu_dp; 628134ef238SVladimir Oltean 629726816a1SVladimir Oltean if (dsa_switch_is_upstream_of(dp->ds, targeted_dp->ds)) 630134ef238SVladimir Oltean return dsa_port_is_dsa(dp) || dp == cpu_dp; 631134ef238SVladimir Oltean 632134ef238SVladimir Oltean return false; 633134ef238SVladimir Oltean } 634134ef238SVladimir Oltean 635134ef238SVladimir Oltean static struct dsa_vlan *dsa_vlan_find(struct list_head *vlan_list, 636134ef238SVladimir Oltean const struct switchdev_obj_port_vlan *vlan) 637134ef238SVladimir Oltean { 638134ef238SVladimir Oltean struct dsa_vlan *v; 639134ef238SVladimir Oltean 640134ef238SVladimir Oltean list_for_each_entry(v, vlan_list, list) 641134ef238SVladimir Oltean if (v->vid == vlan->vid) 642134ef238SVladimir Oltean return v; 643134ef238SVladimir Oltean 644134ef238SVladimir Oltean return NULL; 645134ef238SVladimir Oltean } 646134ef238SVladimir Oltean 647134ef238SVladimir Oltean static int dsa_port_do_vlan_add(struct dsa_port *dp, 648134ef238SVladimir Oltean const struct switchdev_obj_port_vlan *vlan, 649134ef238SVladimir Oltean struct netlink_ext_ack *extack) 650134ef238SVladimir Oltean { 651134ef238SVladimir Oltean struct dsa_switch *ds = dp->ds; 652134ef238SVladimir Oltean int port = dp->index; 653134ef238SVladimir Oltean struct dsa_vlan *v; 654134ef238SVladimir Oltean int err = 0; 655134ef238SVladimir Oltean 656134ef238SVladimir Oltean /* No need to bother with refcounting for user ports. */ 657134ef238SVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 658134ef238SVladimir Oltean return ds->ops->port_vlan_add(ds, port, vlan, extack); 659134ef238SVladimir Oltean 660134ef238SVladimir Oltean /* No need to propagate on shared ports the existing VLANs that were 661134ef238SVladimir Oltean * re-notified after just the flags have changed. This would cause a 662134ef238SVladimir Oltean * refcount bump which we need to avoid, since it unbalances the 663134ef238SVladimir Oltean * additions with the deletions. 664134ef238SVladimir Oltean */ 665134ef238SVladimir Oltean if (vlan->changed) 666134ef238SVladimir Oltean return 0; 667134ef238SVladimir Oltean 668134ef238SVladimir Oltean mutex_lock(&dp->vlans_lock); 669134ef238SVladimir Oltean 670134ef238SVladimir Oltean v = dsa_vlan_find(&dp->vlans, vlan); 671134ef238SVladimir Oltean if (v) { 672134ef238SVladimir Oltean refcount_inc(&v->refcount); 673134ef238SVladimir Oltean goto out; 674134ef238SVladimir Oltean } 675134ef238SVladimir Oltean 676134ef238SVladimir Oltean v = kzalloc(sizeof(*v), GFP_KERNEL); 677134ef238SVladimir Oltean if (!v) { 678134ef238SVladimir Oltean err = -ENOMEM; 679134ef238SVladimir Oltean goto out; 680134ef238SVladimir Oltean } 681134ef238SVladimir Oltean 682134ef238SVladimir Oltean err = ds->ops->port_vlan_add(ds, port, vlan, extack); 683134ef238SVladimir Oltean if (err) { 684134ef238SVladimir Oltean kfree(v); 685134ef238SVladimir Oltean goto out; 686134ef238SVladimir Oltean } 687134ef238SVladimir Oltean 688134ef238SVladimir Oltean v->vid = vlan->vid; 689134ef238SVladimir Oltean refcount_set(&v->refcount, 1); 690134ef238SVladimir Oltean list_add_tail(&v->list, &dp->vlans); 691134ef238SVladimir Oltean 692134ef238SVladimir Oltean out: 693134ef238SVladimir Oltean mutex_unlock(&dp->vlans_lock); 694134ef238SVladimir Oltean 695134ef238SVladimir Oltean return err; 696134ef238SVladimir Oltean } 697134ef238SVladimir Oltean 698134ef238SVladimir Oltean static int dsa_port_do_vlan_del(struct dsa_port *dp, 699134ef238SVladimir Oltean const struct switchdev_obj_port_vlan *vlan) 700134ef238SVladimir Oltean { 701134ef238SVladimir Oltean struct dsa_switch *ds = dp->ds; 702134ef238SVladimir Oltean int port = dp->index; 703134ef238SVladimir Oltean struct dsa_vlan *v; 704134ef238SVladimir Oltean int err = 0; 705134ef238SVladimir Oltean 706134ef238SVladimir Oltean /* No need to bother with refcounting for user ports */ 707134ef238SVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 708134ef238SVladimir Oltean return ds->ops->port_vlan_del(ds, port, vlan); 709134ef238SVladimir Oltean 710134ef238SVladimir Oltean mutex_lock(&dp->vlans_lock); 711134ef238SVladimir Oltean 712134ef238SVladimir Oltean v = dsa_vlan_find(&dp->vlans, vlan); 713134ef238SVladimir Oltean if (!v) { 714134ef238SVladimir Oltean err = -ENOENT; 715134ef238SVladimir Oltean goto out; 716134ef238SVladimir Oltean } 717134ef238SVladimir Oltean 718134ef238SVladimir Oltean if (!refcount_dec_and_test(&v->refcount)) 719134ef238SVladimir Oltean goto out; 720134ef238SVladimir Oltean 721134ef238SVladimir Oltean err = ds->ops->port_vlan_del(ds, port, vlan); 722134ef238SVladimir Oltean if (err) { 723134ef238SVladimir Oltean refcount_set(&v->refcount, 1); 724134ef238SVladimir Oltean goto out; 725134ef238SVladimir Oltean } 726134ef238SVladimir Oltean 727134ef238SVladimir Oltean list_del(&v->list); 728134ef238SVladimir Oltean kfree(v); 729134ef238SVladimir Oltean 730134ef238SVladimir Oltean out: 731134ef238SVladimir Oltean mutex_unlock(&dp->vlans_lock); 732134ef238SVladimir Oltean 733134ef238SVladimir Oltean return err; 734134ef238SVladimir Oltean } 735134ef238SVladimir Oltean 736ffb68fc5SVladimir Oltean static int dsa_switch_vlan_add(struct dsa_switch *ds, 737e65d45ccSVivien Didelot struct dsa_notifier_vlan_info *info) 7389c428c59SVivien Didelot { 739fac6abd5SVladimir Oltean struct dsa_port *dp; 740fac6abd5SVladimir Oltean int err; 7419c428c59SVivien Didelot 7421958d581SVladimir Oltean if (!ds->ops->port_vlan_add) 7439c428c59SVivien Didelot return -EOPNOTSUPP; 7449c428c59SVivien Didelot 745fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 746fac6abd5SVladimir Oltean if (dsa_port_vlan_match(dp, info)) { 747134ef238SVladimir Oltean err = dsa_port_do_vlan_add(dp, info->vlan, 74831046a5fSVladimir Oltean info->extack); 7499c428c59SVivien Didelot if (err) 7509c428c59SVivien Didelot return err; 7519c428c59SVivien Didelot } 752e65d45ccSVivien Didelot } 7539c428c59SVivien Didelot 754d0c627b8SVivien Didelot return 0; 755d0c627b8SVivien Didelot } 756d0c627b8SVivien Didelot 757d0c627b8SVivien Didelot static int dsa_switch_vlan_del(struct dsa_switch *ds, 758d0c627b8SVivien Didelot struct dsa_notifier_vlan_info *info) 759d0c627b8SVivien Didelot { 760134ef238SVladimir Oltean struct dsa_port *dp; 761134ef238SVladimir Oltean int err; 762134ef238SVladimir Oltean 763d0c627b8SVivien Didelot if (!ds->ops->port_vlan_del) 764d0c627b8SVivien Didelot return -EOPNOTSUPP; 765d0c627b8SVivien Didelot 766134ef238SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 767134ef238SVladimir Oltean if (dsa_port_vlan_match(dp, info)) { 768134ef238SVladimir Oltean err = dsa_port_do_vlan_del(dp, info->vlan); 769134ef238SVladimir Oltean if (err) 770134ef238SVladimir Oltean return err; 771134ef238SVladimir Oltean } 772134ef238SVladimir Oltean } 7731ca4aa9cSVivien Didelot 774134ef238SVladimir Oltean return 0; 775134ef238SVladimir Oltean } 776134ef238SVladimir Oltean 777134ef238SVladimir Oltean static int dsa_switch_host_vlan_add(struct dsa_switch *ds, 778134ef238SVladimir Oltean struct dsa_notifier_vlan_info *info) 779134ef238SVladimir Oltean { 780134ef238SVladimir Oltean struct dsa_port *dp; 781134ef238SVladimir Oltean int err; 782134ef238SVladimir Oltean 783134ef238SVladimir Oltean if (!ds->ops->port_vlan_add) 784134ef238SVladimir Oltean return -EOPNOTSUPP; 785134ef238SVladimir Oltean 786134ef238SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 787726816a1SVladimir Oltean if (dsa_port_host_vlan_match(dp, info->dp)) { 788134ef238SVladimir Oltean err = dsa_port_do_vlan_add(dp, info->vlan, 789134ef238SVladimir Oltean info->extack); 790134ef238SVladimir Oltean if (err) 791134ef238SVladimir Oltean return err; 792134ef238SVladimir Oltean } 793134ef238SVladimir Oltean } 794134ef238SVladimir Oltean 795134ef238SVladimir Oltean return 0; 796134ef238SVladimir Oltean } 797134ef238SVladimir Oltean 798134ef238SVladimir Oltean static int dsa_switch_host_vlan_del(struct dsa_switch *ds, 799134ef238SVladimir Oltean struct dsa_notifier_vlan_info *info) 800134ef238SVladimir Oltean { 801134ef238SVladimir Oltean struct dsa_port *dp; 802134ef238SVladimir Oltean int err; 803134ef238SVladimir Oltean 804134ef238SVladimir Oltean if (!ds->ops->port_vlan_del) 805134ef238SVladimir Oltean return -EOPNOTSUPP; 806134ef238SVladimir Oltean 807134ef238SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 808726816a1SVladimir Oltean if (dsa_port_host_vlan_match(dp, info->dp)) { 809134ef238SVladimir Oltean err = dsa_port_do_vlan_del(dp, info->vlan); 810134ef238SVladimir Oltean if (err) 811134ef238SVladimir Oltean return err; 812134ef238SVladimir Oltean } 813134ef238SVladimir Oltean } 814134ef238SVladimir Oltean 8151ca4aa9cSVivien Didelot return 0; 816d0c627b8SVivien Didelot } 817d0c627b8SVivien Didelot 81853da0ebaSVladimir Oltean static int dsa_switch_change_tag_proto(struct dsa_switch *ds, 81953da0ebaSVladimir Oltean struct dsa_notifier_tag_proto_info *info) 82053da0ebaSVladimir Oltean { 82153da0ebaSVladimir Oltean const struct dsa_device_ops *tag_ops = info->tag_ops; 822d0004a02SVladimir Oltean struct dsa_port *dp, *cpu_dp; 823d0004a02SVladimir Oltean int err; 82453da0ebaSVladimir Oltean 82553da0ebaSVladimir Oltean if (!ds->ops->change_tag_protocol) 82653da0ebaSVladimir Oltean return -EOPNOTSUPP; 82753da0ebaSVladimir Oltean 82853da0ebaSVladimir Oltean ASSERT_RTNL(); 82953da0ebaSVladimir Oltean 830bacf93b0SVladimir Oltean err = ds->ops->change_tag_protocol(ds, tag_ops->proto); 83153da0ebaSVladimir Oltean if (err) 83253da0ebaSVladimir Oltean return err; 83353da0ebaSVladimir Oltean 834bacf93b0SVladimir Oltean dsa_switch_for_each_cpu_port(cpu_dp, ds) 835d0004a02SVladimir Oltean dsa_port_set_tag_protocol(cpu_dp, tag_ops); 83653da0ebaSVladimir Oltean 83753da0ebaSVladimir Oltean /* Now that changing the tag protocol can no longer fail, let's update 83853da0ebaSVladimir Oltean * the remaining bits which are "duplicated for faster access", and the 83953da0ebaSVladimir Oltean * bits that depend on the tagger, such as the MTU. 84053da0ebaSVladimir Oltean */ 841d0004a02SVladimir Oltean dsa_switch_for_each_user_port(dp, ds) { 842d0004a02SVladimir Oltean struct net_device *slave = dp->slave; 84353da0ebaSVladimir Oltean 84453da0ebaSVladimir Oltean dsa_slave_setup_tagger(slave); 84553da0ebaSVladimir Oltean 84653da0ebaSVladimir Oltean /* rtnl_mutex is held in dsa_tree_change_tag_proto */ 84753da0ebaSVladimir Oltean dsa_slave_change_mtu(slave, slave->mtu); 84853da0ebaSVladimir Oltean } 84953da0ebaSVladimir Oltean 85053da0ebaSVladimir Oltean return 0; 85153da0ebaSVladimir Oltean } 85253da0ebaSVladimir Oltean 8537f297314SVladimir Oltean /* We use the same cross-chip notifiers to inform both the tagger side, as well 8547f297314SVladimir Oltean * as the switch side, of connection and disconnection events. 8557f297314SVladimir Oltean * Since ds->tagger_data is owned by the tagger, it isn't a hard error if the 8567f297314SVladimir Oltean * switch side doesn't support connecting to this tagger, and therefore, the 8577f297314SVladimir Oltean * fact that we don't disconnect the tagger side doesn't constitute a memory 8587f297314SVladimir Oltean * leak: the tagger will still operate with persistent per-switch memory, just 8597f297314SVladimir Oltean * with the switch side unconnected to it. What does constitute a hard error is 8607f297314SVladimir Oltean * when the switch side supports connecting but fails. 8617f297314SVladimir Oltean */ 8627f297314SVladimir Oltean static int 8637f297314SVladimir Oltean dsa_switch_connect_tag_proto(struct dsa_switch *ds, 864dc452a47SVladimir Oltean struct dsa_notifier_tag_proto_info *info) 865dc452a47SVladimir Oltean { 866dc452a47SVladimir Oltean const struct dsa_device_ops *tag_ops = info->tag_ops; 8677f297314SVladimir Oltean int err; 8687f297314SVladimir Oltean 8697f297314SVladimir Oltean /* Notify the new tagger about the connection to this switch */ 8707f297314SVladimir Oltean if (tag_ops->connect) { 8717f297314SVladimir Oltean err = tag_ops->connect(ds); 8727f297314SVladimir Oltean if (err) 8737f297314SVladimir Oltean return err; 8747f297314SVladimir Oltean } 875dc452a47SVladimir Oltean 876dc452a47SVladimir Oltean if (!ds->ops->connect_tag_protocol) 877dc452a47SVladimir Oltean return -EOPNOTSUPP; 878dc452a47SVladimir Oltean 8797f297314SVladimir Oltean /* Notify the switch about the connection to the new tagger */ 8807f297314SVladimir Oltean err = ds->ops->connect_tag_protocol(ds, tag_ops->proto); 8817f297314SVladimir Oltean if (err) { 8827f297314SVladimir Oltean /* Revert the new tagger's connection to this tree */ 8837f297314SVladimir Oltean if (tag_ops->disconnect) 8847f297314SVladimir Oltean tag_ops->disconnect(ds); 8857f297314SVladimir Oltean return err; 8867f297314SVladimir Oltean } 8877f297314SVladimir Oltean 8887f297314SVladimir Oltean return 0; 8897f297314SVladimir Oltean } 8907f297314SVladimir Oltean 8917f297314SVladimir Oltean static int 8927f297314SVladimir Oltean dsa_switch_disconnect_tag_proto(struct dsa_switch *ds, 8937f297314SVladimir Oltean struct dsa_notifier_tag_proto_info *info) 8947f297314SVladimir Oltean { 8957f297314SVladimir Oltean const struct dsa_device_ops *tag_ops = info->tag_ops; 8967f297314SVladimir Oltean 8977f297314SVladimir Oltean /* Notify the tagger about the disconnection from this switch */ 8987f297314SVladimir Oltean if (tag_ops->disconnect && ds->tagger_data) 8997f297314SVladimir Oltean tag_ops->disconnect(ds); 9007f297314SVladimir Oltean 9017f297314SVladimir Oltean /* No need to notify the switch, since it shouldn't have any 9027f297314SVladimir Oltean * resources to tear down 9037f297314SVladimir Oltean */ 9047f297314SVladimir Oltean return 0; 905dc452a47SVladimir Oltean } 906dc452a47SVladimir Oltean 907295ab96fSVladimir Oltean static int 908295ab96fSVladimir Oltean dsa_switch_master_state_change(struct dsa_switch *ds, 909295ab96fSVladimir Oltean struct dsa_notifier_master_state_info *info) 910295ab96fSVladimir Oltean { 911295ab96fSVladimir Oltean if (!ds->ops->master_state_change) 912295ab96fSVladimir Oltean return 0; 913295ab96fSVladimir Oltean 914295ab96fSVladimir Oltean ds->ops->master_state_change(ds, info->master, info->operational); 915295ab96fSVladimir Oltean 916295ab96fSVladimir Oltean return 0; 917295ab96fSVladimir Oltean } 918295ab96fSVladimir Oltean 919f515f192SVivien Didelot static int dsa_switch_event(struct notifier_block *nb, 920f515f192SVivien Didelot unsigned long event, void *info) 921f515f192SVivien Didelot { 922f515f192SVivien Didelot struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb); 923f515f192SVivien Didelot int err; 924f515f192SVivien Didelot 925f515f192SVivien Didelot switch (event) { 9261faabf74SVivien Didelot case DSA_NOTIFIER_AGEING_TIME: 9271faabf74SVivien Didelot err = dsa_switch_ageing_time(ds, info); 9281faabf74SVivien Didelot break; 92904d3a4c6SVivien Didelot case DSA_NOTIFIER_BRIDGE_JOIN: 93004d3a4c6SVivien Didelot err = dsa_switch_bridge_join(ds, info); 93104d3a4c6SVivien Didelot break; 93204d3a4c6SVivien Didelot case DSA_NOTIFIER_BRIDGE_LEAVE: 93304d3a4c6SVivien Didelot err = dsa_switch_bridge_leave(ds, info); 93404d3a4c6SVivien Didelot break; 935685fb6a4SVivien Didelot case DSA_NOTIFIER_FDB_ADD: 936685fb6a4SVivien Didelot err = dsa_switch_fdb_add(ds, info); 937685fb6a4SVivien Didelot break; 938685fb6a4SVivien Didelot case DSA_NOTIFIER_FDB_DEL: 939685fb6a4SVivien Didelot err = dsa_switch_fdb_del(ds, info); 940685fb6a4SVivien Didelot break; 9413dc80afcSVladimir Oltean case DSA_NOTIFIER_HOST_FDB_ADD: 9423dc80afcSVladimir Oltean err = dsa_switch_host_fdb_add(ds, info); 9433dc80afcSVladimir Oltean break; 9443dc80afcSVladimir Oltean case DSA_NOTIFIER_HOST_FDB_DEL: 9453dc80afcSVladimir Oltean err = dsa_switch_host_fdb_del(ds, info); 9463dc80afcSVladimir Oltean break; 947e212fa7cSVladimir Oltean case DSA_NOTIFIER_LAG_FDB_ADD: 948e212fa7cSVladimir Oltean err = dsa_switch_lag_fdb_add(ds, info); 949e212fa7cSVladimir Oltean break; 950e212fa7cSVladimir Oltean case DSA_NOTIFIER_LAG_FDB_DEL: 951e212fa7cSVladimir Oltean err = dsa_switch_lag_fdb_del(ds, info); 952e212fa7cSVladimir Oltean break; 953058102a6STobias Waldekranz case DSA_NOTIFIER_LAG_CHANGE: 954058102a6STobias Waldekranz err = dsa_switch_lag_change(ds, info); 955058102a6STobias Waldekranz break; 956058102a6STobias Waldekranz case DSA_NOTIFIER_LAG_JOIN: 957058102a6STobias Waldekranz err = dsa_switch_lag_join(ds, info); 958058102a6STobias Waldekranz break; 959058102a6STobias Waldekranz case DSA_NOTIFIER_LAG_LEAVE: 960058102a6STobias Waldekranz err = dsa_switch_lag_leave(ds, info); 961058102a6STobias Waldekranz break; 9628ae5bcdcSVivien Didelot case DSA_NOTIFIER_MDB_ADD: 9638ae5bcdcSVivien Didelot err = dsa_switch_mdb_add(ds, info); 9648ae5bcdcSVivien Didelot break; 9658ae5bcdcSVivien Didelot case DSA_NOTIFIER_MDB_DEL: 9668ae5bcdcSVivien Didelot err = dsa_switch_mdb_del(ds, info); 9678ae5bcdcSVivien Didelot break; 968b8e997c4SVladimir Oltean case DSA_NOTIFIER_HOST_MDB_ADD: 969b8e997c4SVladimir Oltean err = dsa_switch_host_mdb_add(ds, info); 970b8e997c4SVladimir Oltean break; 971b8e997c4SVladimir Oltean case DSA_NOTIFIER_HOST_MDB_DEL: 972b8e997c4SVladimir Oltean err = dsa_switch_host_mdb_del(ds, info); 973b8e997c4SVladimir Oltean break; 974d0c627b8SVivien Didelot case DSA_NOTIFIER_VLAN_ADD: 975d0c627b8SVivien Didelot err = dsa_switch_vlan_add(ds, info); 976d0c627b8SVivien Didelot break; 977d0c627b8SVivien Didelot case DSA_NOTIFIER_VLAN_DEL: 978d0c627b8SVivien Didelot err = dsa_switch_vlan_del(ds, info); 979d0c627b8SVivien Didelot break; 980134ef238SVladimir Oltean case DSA_NOTIFIER_HOST_VLAN_ADD: 981134ef238SVladimir Oltean err = dsa_switch_host_vlan_add(ds, info); 982134ef238SVladimir Oltean break; 983134ef238SVladimir Oltean case DSA_NOTIFIER_HOST_VLAN_DEL: 984134ef238SVladimir Oltean err = dsa_switch_host_vlan_del(ds, info); 985134ef238SVladimir Oltean break; 986bfcb8132SVladimir Oltean case DSA_NOTIFIER_MTU: 987bfcb8132SVladimir Oltean err = dsa_switch_mtu(ds, info); 988bfcb8132SVladimir Oltean break; 98953da0ebaSVladimir Oltean case DSA_NOTIFIER_TAG_PROTO: 99053da0ebaSVladimir Oltean err = dsa_switch_change_tag_proto(ds, info); 99153da0ebaSVladimir Oltean break; 992dc452a47SVladimir Oltean case DSA_NOTIFIER_TAG_PROTO_CONNECT: 993dc452a47SVladimir Oltean err = dsa_switch_connect_tag_proto(ds, info); 994dc452a47SVladimir Oltean break; 9957f297314SVladimir Oltean case DSA_NOTIFIER_TAG_PROTO_DISCONNECT: 9967f297314SVladimir Oltean err = dsa_switch_disconnect_tag_proto(ds, info); 9977f297314SVladimir Oltean break; 998c64b9c05SVladimir Oltean case DSA_NOTIFIER_TAG_8021Q_VLAN_ADD: 999c64b9c05SVladimir Oltean err = dsa_switch_tag_8021q_vlan_add(ds, info); 1000c64b9c05SVladimir Oltean break; 1001c64b9c05SVladimir Oltean case DSA_NOTIFIER_TAG_8021Q_VLAN_DEL: 1002c64b9c05SVladimir Oltean err = dsa_switch_tag_8021q_vlan_del(ds, info); 1003c64b9c05SVladimir Oltean break; 1004295ab96fSVladimir Oltean case DSA_NOTIFIER_MASTER_STATE_CHANGE: 1005295ab96fSVladimir Oltean err = dsa_switch_master_state_change(ds, info); 1006295ab96fSVladimir Oltean break; 1007f515f192SVivien Didelot default: 1008f515f192SVivien Didelot err = -EOPNOTSUPP; 1009f515f192SVivien Didelot break; 1010f515f192SVivien Didelot } 1011f515f192SVivien Didelot 1012f515f192SVivien Didelot if (err) 1013f515f192SVivien Didelot dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n", 1014f515f192SVivien Didelot event, err); 1015f515f192SVivien Didelot 1016f515f192SVivien Didelot return notifier_from_errno(err); 1017f515f192SVivien Didelot } 1018f515f192SVivien Didelot 1019f515f192SVivien Didelot int dsa_switch_register_notifier(struct dsa_switch *ds) 1020f515f192SVivien Didelot { 1021f515f192SVivien Didelot ds->nb.notifier_call = dsa_switch_event; 1022f515f192SVivien Didelot 1023f515f192SVivien Didelot return raw_notifier_chain_register(&ds->dst->nh, &ds->nb); 1024f515f192SVivien Didelot } 1025f515f192SVivien Didelot 1026f515f192SVivien Didelot void dsa_switch_unregister_notifier(struct dsa_switch *ds) 1027f515f192SVivien Didelot { 1028f515f192SVivien Didelot int err; 1029f515f192SVivien Didelot 1030f515f192SVivien Didelot err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb); 1031f515f192SVivien Didelot if (err) 1032f515f192SVivien Didelot dev_err(ds->dev, "failed to unregister notifier (%d)\n", err); 1033f515f192SVivien Didelot } 1034