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 15*47d2ce03SVladimir Oltean #include "dsa.h" 16ea5dd34bSVivien Didelot #include "dsa_priv.h" 17022bba63SVladimir Oltean #include "port.h" 1809f92341SVladimir Oltean #include "slave.h" 190c603136SVladimir Oltean #include "switch.h" 20f515f192SVivien Didelot 211faabf74SVivien Didelot static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds, 221faabf74SVivien Didelot unsigned int ageing_time) 231faabf74SVivien Didelot { 24d0004a02SVladimir Oltean struct dsa_port *dp; 251faabf74SVivien Didelot 26d0004a02SVladimir Oltean dsa_switch_for_each_port(dp, ds) 271faabf74SVivien Didelot if (dp->ageing_time && dp->ageing_time < ageing_time) 281faabf74SVivien Didelot ageing_time = dp->ageing_time; 291faabf74SVivien Didelot 301faabf74SVivien Didelot return ageing_time; 311faabf74SVivien Didelot } 321faabf74SVivien Didelot 331faabf74SVivien Didelot static int dsa_switch_ageing_time(struct dsa_switch *ds, 341faabf74SVivien Didelot struct dsa_notifier_ageing_time_info *info) 351faabf74SVivien Didelot { 361faabf74SVivien Didelot unsigned int ageing_time = info->ageing_time; 371faabf74SVivien Didelot 381faabf74SVivien Didelot if (ds->ageing_time_min && ageing_time < ds->ageing_time_min) 391faabf74SVivien Didelot return -ERANGE; 4077b61365SVladimir Oltean 411faabf74SVivien Didelot if (ds->ageing_time_max && ageing_time > ds->ageing_time_max) 421faabf74SVivien Didelot return -ERANGE; 431faabf74SVivien Didelot 441faabf74SVivien Didelot /* Program the fastest ageing time in case of multiple bridges */ 451faabf74SVivien Didelot ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time); 461faabf74SVivien Didelot 471faabf74SVivien Didelot if (ds->ops->set_ageing_time) 481faabf74SVivien Didelot return ds->ops->set_ageing_time(ds, ageing_time); 491faabf74SVivien Didelot 501faabf74SVivien Didelot return 0; 511faabf74SVivien Didelot } 521faabf74SVivien Didelot 53fac6abd5SVladimir Oltean static bool dsa_port_mtu_match(struct dsa_port *dp, 54bfcb8132SVladimir Oltean struct dsa_notifier_mtu_info *info) 55bfcb8132SVladimir Oltean { 56be6ff966SVladimir Oltean return dp == info->dp || dsa_port_is_dsa(dp) || dsa_port_is_cpu(dp); 57bfcb8132SVladimir Oltean } 58bfcb8132SVladimir Oltean 59bfcb8132SVladimir Oltean static int dsa_switch_mtu(struct dsa_switch *ds, 60bfcb8132SVladimir Oltean struct dsa_notifier_mtu_info *info) 61bfcb8132SVladimir Oltean { 62fac6abd5SVladimir Oltean struct dsa_port *dp; 63fac6abd5SVladimir Oltean int ret; 64bfcb8132SVladimir Oltean 65bfcb8132SVladimir Oltean if (!ds->ops->port_change_mtu) 66bfcb8132SVladimir Oltean return -EOPNOTSUPP; 67bfcb8132SVladimir Oltean 68fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 69fac6abd5SVladimir Oltean if (dsa_port_mtu_match(dp, info)) { 70fac6abd5SVladimir Oltean ret = ds->ops->port_change_mtu(ds, dp->index, 71fac6abd5SVladimir Oltean info->mtu); 72bfcb8132SVladimir Oltean if (ret) 73bfcb8132SVladimir Oltean return ret; 74bfcb8132SVladimir Oltean } 75bfcb8132SVladimir Oltean } 76bfcb8132SVladimir Oltean 77bfcb8132SVladimir Oltean return 0; 78bfcb8132SVladimir Oltean } 79bfcb8132SVladimir Oltean 8004d3a4c6SVivien Didelot static int dsa_switch_bridge_join(struct dsa_switch *ds, 8104d3a4c6SVivien Didelot struct dsa_notifier_bridge_info *info) 8204d3a4c6SVivien Didelot { 83e19cc13cSVladimir Oltean int err; 84f66a6a69SVladimir Oltean 85726816a1SVladimir Oltean if (info->dp->ds == ds) { 8667b5fb5dSVladimir Oltean if (!ds->ops->port_bridge_join) 8767b5fb5dSVladimir Oltean return -EOPNOTSUPP; 8867b5fb5dSVladimir Oltean 89726816a1SVladimir Oltean err = ds->ops->port_bridge_join(ds, info->dp->index, 90726816a1SVladimir Oltean info->bridge, 9106b9cce4SVladimir Oltean &info->tx_fwd_offload, 9206b9cce4SVladimir Oltean info->extack); 93e19cc13cSVladimir Oltean if (err) 94e19cc13cSVladimir Oltean return err; 95e19cc13cSVladimir Oltean } 9604d3a4c6SVivien Didelot 97726816a1SVladimir Oltean if (info->dp->ds != ds && ds->ops->crosschip_bridge_join) { 98726816a1SVladimir Oltean err = ds->ops->crosschip_bridge_join(ds, 99726816a1SVladimir Oltean info->dp->ds->dst->index, 100726816a1SVladimir Oltean info->dp->ds->index, 101726816a1SVladimir Oltean info->dp->index, 102726816a1SVladimir Oltean info->bridge, 10306b9cce4SVladimir Oltean info->extack); 104e19cc13cSVladimir Oltean if (err) 105e19cc13cSVladimir Oltean return err; 106e19cc13cSVladimir Oltean } 10704d3a4c6SVivien Didelot 10891495f21SVladimir Oltean return 0; 10904d3a4c6SVivien Didelot } 11004d3a4c6SVivien Didelot 111381a7301STobias Waldekranz static int dsa_switch_bridge_leave(struct dsa_switch *ds, 112381a7301STobias Waldekranz struct dsa_notifier_bridge_info *info) 113381a7301STobias Waldekranz { 114726816a1SVladimir Oltean if (info->dp->ds == ds && ds->ops->port_bridge_leave) 115726816a1SVladimir Oltean ds->ops->port_bridge_leave(ds, info->dp->index, info->bridge); 116381a7301STobias Waldekranz 117726816a1SVladimir Oltean if (info->dp->ds != ds && ds->ops->crosschip_bridge_leave) 118726816a1SVladimir Oltean ds->ops->crosschip_bridge_leave(ds, info->dp->ds->dst->index, 119726816a1SVladimir Oltean info->dp->ds->index, 120726816a1SVladimir Oltean info->dp->index, 121381a7301STobias Waldekranz info->bridge); 122381a7301STobias Waldekranz 12391495f21SVladimir Oltean return 0; 12404d3a4c6SVivien Didelot } 12504d3a4c6SVivien Didelot 126b8e997c4SVladimir Oltean /* Matches for all upstream-facing ports (the CPU port and all upstream-facing 127b8e997c4SVladimir Oltean * DSA links) that sit between the targeted port on which the notifier was 128b8e997c4SVladimir Oltean * emitted and its dedicated CPU port. 129b8e997c4SVladimir Oltean */ 130fac6abd5SVladimir Oltean static bool dsa_port_host_address_match(struct dsa_port *dp, 131726816a1SVladimir Oltean const struct dsa_port *targeted_dp) 132b8e997c4SVladimir Oltean { 133726816a1SVladimir Oltean struct dsa_port *cpu_dp = targeted_dp->cpu_dp; 134b8e997c4SVladimir Oltean 135726816a1SVladimir Oltean if (dsa_switch_is_upstream_of(dp->ds, targeted_dp->ds)) 136fac6abd5SVladimir Oltean return dp->index == dsa_towards_port(dp->ds, cpu_dp->ds->index, 137b8e997c4SVladimir Oltean cpu_dp->index); 138b8e997c4SVladimir Oltean 139b8e997c4SVladimir Oltean return false; 140b8e997c4SVladimir Oltean } 141b8e997c4SVladimir Oltean 142161ca59dSVladimir Oltean static struct dsa_mac_addr *dsa_mac_addr_find(struct list_head *addr_list, 143c2693363SVladimir Oltean const unsigned char *addr, u16 vid, 144c2693363SVladimir Oltean struct dsa_db db) 145161ca59dSVladimir Oltean { 146161ca59dSVladimir Oltean struct dsa_mac_addr *a; 147161ca59dSVladimir Oltean 148161ca59dSVladimir Oltean list_for_each_entry(a, addr_list, list) 149c2693363SVladimir Oltean if (ether_addr_equal(a->addr, addr) && a->vid == vid && 150c2693363SVladimir Oltean dsa_db_equal(&a->db, &db)) 151161ca59dSVladimir Oltean return a; 152161ca59dSVladimir Oltean 153161ca59dSVladimir Oltean return NULL; 154161ca59dSVladimir Oltean } 155161ca59dSVladimir Oltean 156fac6abd5SVladimir Oltean static int dsa_port_do_mdb_add(struct dsa_port *dp, 157c2693363SVladimir Oltean const struct switchdev_obj_port_mdb *mdb, 158c2693363SVladimir Oltean struct dsa_db db) 159161ca59dSVladimir Oltean { 160fac6abd5SVladimir Oltean struct dsa_switch *ds = dp->ds; 161161ca59dSVladimir Oltean struct dsa_mac_addr *a; 162fac6abd5SVladimir Oltean int port = dp->index; 163338a3a47SVladimir Oltean int err = 0; 164161ca59dSVladimir Oltean 165161ca59dSVladimir Oltean /* No need to bother with refcounting for user ports */ 166161ca59dSVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 167c2693363SVladimir Oltean return ds->ops->port_mdb_add(ds, port, mdb, db); 168161ca59dSVladimir Oltean 169338a3a47SVladimir Oltean mutex_lock(&dp->addr_lists_lock); 170338a3a47SVladimir Oltean 171c2693363SVladimir Oltean a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid, db); 172161ca59dSVladimir Oltean if (a) { 173161ca59dSVladimir Oltean refcount_inc(&a->refcount); 174338a3a47SVladimir Oltean goto out; 175161ca59dSVladimir Oltean } 176161ca59dSVladimir Oltean 177161ca59dSVladimir Oltean a = kzalloc(sizeof(*a), GFP_KERNEL); 178338a3a47SVladimir Oltean if (!a) { 179338a3a47SVladimir Oltean err = -ENOMEM; 180338a3a47SVladimir Oltean goto out; 181338a3a47SVladimir Oltean } 182161ca59dSVladimir Oltean 183c2693363SVladimir Oltean err = ds->ops->port_mdb_add(ds, port, mdb, db); 184161ca59dSVladimir Oltean if (err) { 185161ca59dSVladimir Oltean kfree(a); 186338a3a47SVladimir Oltean goto out; 187161ca59dSVladimir Oltean } 188161ca59dSVladimir Oltean 189161ca59dSVladimir Oltean ether_addr_copy(a->addr, mdb->addr); 190161ca59dSVladimir Oltean a->vid = mdb->vid; 191c2693363SVladimir Oltean a->db = db; 192161ca59dSVladimir Oltean refcount_set(&a->refcount, 1); 193161ca59dSVladimir Oltean list_add_tail(&a->list, &dp->mdbs); 194161ca59dSVladimir Oltean 195338a3a47SVladimir Oltean out: 196338a3a47SVladimir Oltean mutex_unlock(&dp->addr_lists_lock); 197338a3a47SVladimir Oltean 198338a3a47SVladimir Oltean return err; 199161ca59dSVladimir Oltean } 200161ca59dSVladimir Oltean 201fac6abd5SVladimir Oltean static int dsa_port_do_mdb_del(struct dsa_port *dp, 202c2693363SVladimir Oltean const struct switchdev_obj_port_mdb *mdb, 203c2693363SVladimir Oltean struct dsa_db db) 204161ca59dSVladimir Oltean { 205fac6abd5SVladimir Oltean struct dsa_switch *ds = dp->ds; 206161ca59dSVladimir Oltean struct dsa_mac_addr *a; 207fac6abd5SVladimir Oltean int port = dp->index; 208338a3a47SVladimir Oltean int err = 0; 209161ca59dSVladimir Oltean 210161ca59dSVladimir Oltean /* No need to bother with refcounting for user ports */ 211161ca59dSVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 212c2693363SVladimir Oltean return ds->ops->port_mdb_del(ds, port, mdb, db); 213161ca59dSVladimir Oltean 214338a3a47SVladimir Oltean mutex_lock(&dp->addr_lists_lock); 215338a3a47SVladimir Oltean 216c2693363SVladimir Oltean a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid, db); 217338a3a47SVladimir Oltean if (!a) { 218338a3a47SVladimir Oltean err = -ENOENT; 219338a3a47SVladimir Oltean goto out; 220338a3a47SVladimir Oltean } 221161ca59dSVladimir Oltean 222161ca59dSVladimir Oltean if (!refcount_dec_and_test(&a->refcount)) 223338a3a47SVladimir Oltean goto out; 224161ca59dSVladimir Oltean 225c2693363SVladimir Oltean err = ds->ops->port_mdb_del(ds, port, mdb, db); 226161ca59dSVladimir Oltean if (err) { 227232deb3fSVladimir Oltean refcount_set(&a->refcount, 1); 228338a3a47SVladimir Oltean goto out; 229161ca59dSVladimir Oltean } 230161ca59dSVladimir Oltean 231161ca59dSVladimir Oltean list_del(&a->list); 232161ca59dSVladimir Oltean kfree(a); 233161ca59dSVladimir Oltean 234338a3a47SVladimir Oltean out: 235338a3a47SVladimir Oltean mutex_unlock(&dp->addr_lists_lock); 236338a3a47SVladimir Oltean 237338a3a47SVladimir Oltean return err; 238161ca59dSVladimir Oltean } 239161ca59dSVladimir Oltean 240fac6abd5SVladimir Oltean static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr, 241c2693363SVladimir Oltean u16 vid, struct dsa_db db) 2423f6e32f9SVladimir Oltean { 243fac6abd5SVladimir Oltean struct dsa_switch *ds = dp->ds; 2443f6e32f9SVladimir Oltean struct dsa_mac_addr *a; 245fac6abd5SVladimir Oltean int port = dp->index; 246338a3a47SVladimir Oltean int err = 0; 2473f6e32f9SVladimir Oltean 2483f6e32f9SVladimir Oltean /* No need to bother with refcounting for user ports */ 2493f6e32f9SVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 250c2693363SVladimir Oltean return ds->ops->port_fdb_add(ds, port, addr, vid, db); 2513f6e32f9SVladimir Oltean 252338a3a47SVladimir Oltean mutex_lock(&dp->addr_lists_lock); 253338a3a47SVladimir Oltean 254c2693363SVladimir Oltean a = dsa_mac_addr_find(&dp->fdbs, addr, vid, db); 2553f6e32f9SVladimir Oltean if (a) { 2563f6e32f9SVladimir Oltean refcount_inc(&a->refcount); 257338a3a47SVladimir Oltean goto out; 2583f6e32f9SVladimir Oltean } 2593f6e32f9SVladimir Oltean 2603f6e32f9SVladimir Oltean a = kzalloc(sizeof(*a), GFP_KERNEL); 261338a3a47SVladimir Oltean if (!a) { 262338a3a47SVladimir Oltean err = -ENOMEM; 263338a3a47SVladimir Oltean goto out; 264338a3a47SVladimir Oltean } 2653f6e32f9SVladimir Oltean 266c2693363SVladimir Oltean err = ds->ops->port_fdb_add(ds, port, addr, vid, db); 2673f6e32f9SVladimir Oltean if (err) { 2683f6e32f9SVladimir Oltean kfree(a); 269338a3a47SVladimir Oltean goto out; 2703f6e32f9SVladimir Oltean } 2713f6e32f9SVladimir Oltean 2723f6e32f9SVladimir Oltean ether_addr_copy(a->addr, addr); 2733f6e32f9SVladimir Oltean a->vid = vid; 274c2693363SVladimir Oltean a->db = db; 2753f6e32f9SVladimir Oltean refcount_set(&a->refcount, 1); 2763f6e32f9SVladimir Oltean list_add_tail(&a->list, &dp->fdbs); 2773f6e32f9SVladimir Oltean 278338a3a47SVladimir Oltean out: 279338a3a47SVladimir Oltean mutex_unlock(&dp->addr_lists_lock); 280338a3a47SVladimir Oltean 281338a3a47SVladimir Oltean return err; 2823f6e32f9SVladimir Oltean } 2833f6e32f9SVladimir Oltean 284fac6abd5SVladimir Oltean static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr, 285c2693363SVladimir Oltean u16 vid, struct dsa_db db) 2863f6e32f9SVladimir Oltean { 287fac6abd5SVladimir Oltean struct dsa_switch *ds = dp->ds; 2883f6e32f9SVladimir Oltean struct dsa_mac_addr *a; 289fac6abd5SVladimir Oltean int port = dp->index; 290338a3a47SVladimir Oltean int err = 0; 2913f6e32f9SVladimir Oltean 2923f6e32f9SVladimir Oltean /* No need to bother with refcounting for user ports */ 2933f6e32f9SVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 294c2693363SVladimir Oltean return ds->ops->port_fdb_del(ds, port, addr, vid, db); 2953f6e32f9SVladimir Oltean 296338a3a47SVladimir Oltean mutex_lock(&dp->addr_lists_lock); 297338a3a47SVladimir Oltean 298c2693363SVladimir Oltean a = dsa_mac_addr_find(&dp->fdbs, addr, vid, db); 299338a3a47SVladimir Oltean if (!a) { 300338a3a47SVladimir Oltean err = -ENOENT; 301338a3a47SVladimir Oltean goto out; 302338a3a47SVladimir Oltean } 3033f6e32f9SVladimir Oltean 3043f6e32f9SVladimir Oltean if (!refcount_dec_and_test(&a->refcount)) 305338a3a47SVladimir Oltean goto out; 3063f6e32f9SVladimir Oltean 307c2693363SVladimir Oltean err = ds->ops->port_fdb_del(ds, port, addr, vid, db); 3083f6e32f9SVladimir Oltean if (err) { 309232deb3fSVladimir Oltean refcount_set(&a->refcount, 1); 310338a3a47SVladimir Oltean goto out; 3113f6e32f9SVladimir Oltean } 3123f6e32f9SVladimir Oltean 3133f6e32f9SVladimir Oltean list_del(&a->list); 3143f6e32f9SVladimir Oltean kfree(a); 3153f6e32f9SVladimir Oltean 316338a3a47SVladimir Oltean out: 317338a3a47SVladimir Oltean mutex_unlock(&dp->addr_lists_lock); 318338a3a47SVladimir Oltean 319338a3a47SVladimir Oltean return err; 3203f6e32f9SVladimir Oltean } 3213f6e32f9SVladimir Oltean 322e212fa7cSVladimir Oltean static int dsa_switch_do_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag *lag, 323c2693363SVladimir Oltean const unsigned char *addr, u16 vid, 324c2693363SVladimir Oltean struct dsa_db db) 325e212fa7cSVladimir Oltean { 326e212fa7cSVladimir Oltean struct dsa_mac_addr *a; 327e212fa7cSVladimir Oltean int err = 0; 328e212fa7cSVladimir Oltean 329e212fa7cSVladimir Oltean mutex_lock(&lag->fdb_lock); 330e212fa7cSVladimir Oltean 331c2693363SVladimir Oltean a = dsa_mac_addr_find(&lag->fdbs, addr, vid, db); 332e212fa7cSVladimir Oltean if (a) { 333e212fa7cSVladimir Oltean refcount_inc(&a->refcount); 334e212fa7cSVladimir Oltean goto out; 335e212fa7cSVladimir Oltean } 336e212fa7cSVladimir Oltean 337e212fa7cSVladimir Oltean a = kzalloc(sizeof(*a), GFP_KERNEL); 338e212fa7cSVladimir Oltean if (!a) { 339e212fa7cSVladimir Oltean err = -ENOMEM; 340e212fa7cSVladimir Oltean goto out; 341e212fa7cSVladimir Oltean } 342e212fa7cSVladimir Oltean 343c2693363SVladimir Oltean err = ds->ops->lag_fdb_add(ds, *lag, addr, vid, db); 344e212fa7cSVladimir Oltean if (err) { 345e212fa7cSVladimir Oltean kfree(a); 346e212fa7cSVladimir Oltean goto out; 347e212fa7cSVladimir Oltean } 348e212fa7cSVladimir Oltean 349e212fa7cSVladimir Oltean ether_addr_copy(a->addr, addr); 350e212fa7cSVladimir Oltean a->vid = vid; 351c7560d12SVladimir Oltean a->db = db; 352e212fa7cSVladimir Oltean refcount_set(&a->refcount, 1); 353e212fa7cSVladimir Oltean list_add_tail(&a->list, &lag->fdbs); 354e212fa7cSVladimir Oltean 355e212fa7cSVladimir Oltean out: 356e212fa7cSVladimir Oltean mutex_unlock(&lag->fdb_lock); 357e212fa7cSVladimir Oltean 358e212fa7cSVladimir Oltean return err; 359e212fa7cSVladimir Oltean } 360e212fa7cSVladimir Oltean 361e212fa7cSVladimir Oltean static int dsa_switch_do_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag *lag, 362c2693363SVladimir Oltean const unsigned char *addr, u16 vid, 363c2693363SVladimir Oltean struct dsa_db db) 364e212fa7cSVladimir Oltean { 365e212fa7cSVladimir Oltean struct dsa_mac_addr *a; 366e212fa7cSVladimir Oltean int err = 0; 367e212fa7cSVladimir Oltean 368e212fa7cSVladimir Oltean mutex_lock(&lag->fdb_lock); 369e212fa7cSVladimir Oltean 370c2693363SVladimir Oltean a = dsa_mac_addr_find(&lag->fdbs, addr, vid, db); 371e212fa7cSVladimir Oltean if (!a) { 372e212fa7cSVladimir Oltean err = -ENOENT; 373e212fa7cSVladimir Oltean goto out; 374e212fa7cSVladimir Oltean } 375e212fa7cSVladimir Oltean 376e212fa7cSVladimir Oltean if (!refcount_dec_and_test(&a->refcount)) 377e212fa7cSVladimir Oltean goto out; 378e212fa7cSVladimir Oltean 379c2693363SVladimir Oltean err = ds->ops->lag_fdb_del(ds, *lag, addr, vid, db); 380e212fa7cSVladimir Oltean if (err) { 381e212fa7cSVladimir Oltean refcount_set(&a->refcount, 1); 382e212fa7cSVladimir Oltean goto out; 383e212fa7cSVladimir Oltean } 384e212fa7cSVladimir Oltean 385e212fa7cSVladimir Oltean list_del(&a->list); 386e212fa7cSVladimir Oltean kfree(a); 387e212fa7cSVladimir Oltean 388e212fa7cSVladimir Oltean out: 389e212fa7cSVladimir Oltean mutex_unlock(&lag->fdb_lock); 390e212fa7cSVladimir Oltean 391e212fa7cSVladimir Oltean return err; 392e212fa7cSVladimir Oltean } 393e212fa7cSVladimir Oltean 3943dc80afcSVladimir Oltean static int dsa_switch_host_fdb_add(struct dsa_switch *ds, 3953dc80afcSVladimir Oltean struct dsa_notifier_fdb_info *info) 3963dc80afcSVladimir Oltean { 397fac6abd5SVladimir Oltean struct dsa_port *dp; 3983dc80afcSVladimir Oltean int err = 0; 3993dc80afcSVladimir Oltean 4003dc80afcSVladimir Oltean if (!ds->ops->port_fdb_add) 4013dc80afcSVladimir Oltean return -EOPNOTSUPP; 4023dc80afcSVladimir Oltean 403fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 404726816a1SVladimir Oltean if (dsa_port_host_address_match(dp, info->dp)) { 405acc43b7bSVladimir Oltean if (dsa_port_is_cpu(dp) && info->dp->cpu_port_in_lag) { 406acc43b7bSVladimir Oltean err = dsa_switch_do_lag_fdb_add(ds, dp->lag, 407acc43b7bSVladimir Oltean info->addr, 408acc43b7bSVladimir Oltean info->vid, 409c2693363SVladimir Oltean info->db); 410acc43b7bSVladimir Oltean } else { 411acc43b7bSVladimir Oltean err = dsa_port_do_fdb_add(dp, info->addr, 412acc43b7bSVladimir Oltean info->vid, info->db); 413acc43b7bSVladimir Oltean } 4143dc80afcSVladimir Oltean if (err) 4153dc80afcSVladimir Oltean break; 4163dc80afcSVladimir Oltean } 4173dc80afcSVladimir Oltean } 4183dc80afcSVladimir Oltean 4193dc80afcSVladimir Oltean return err; 4203dc80afcSVladimir Oltean } 4213dc80afcSVladimir Oltean 4223dc80afcSVladimir Oltean static int dsa_switch_host_fdb_del(struct dsa_switch *ds, 4233dc80afcSVladimir Oltean struct dsa_notifier_fdb_info *info) 4243dc80afcSVladimir Oltean { 425fac6abd5SVladimir Oltean struct dsa_port *dp; 4263f6e32f9SVladimir Oltean int err = 0; 4273f6e32f9SVladimir Oltean 4283dc80afcSVladimir Oltean if (!ds->ops->port_fdb_del) 4293dc80afcSVladimir Oltean return -EOPNOTSUPP; 4303dc80afcSVladimir Oltean 431fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 432726816a1SVladimir Oltean if (dsa_port_host_address_match(dp, info->dp)) { 433acc43b7bSVladimir Oltean if (dsa_port_is_cpu(dp) && info->dp->cpu_port_in_lag) { 434acc43b7bSVladimir Oltean err = dsa_switch_do_lag_fdb_del(ds, dp->lag, 435acc43b7bSVladimir Oltean info->addr, 436acc43b7bSVladimir Oltean info->vid, 437c2693363SVladimir Oltean info->db); 438acc43b7bSVladimir Oltean } else { 439acc43b7bSVladimir Oltean err = dsa_port_do_fdb_del(dp, info->addr, 440acc43b7bSVladimir Oltean info->vid, info->db); 441acc43b7bSVladimir Oltean } 4423f6e32f9SVladimir Oltean if (err) 4433f6e32f9SVladimir Oltean break; 4443f6e32f9SVladimir Oltean } 4453f6e32f9SVladimir Oltean } 4463dc80afcSVladimir Oltean 4473f6e32f9SVladimir Oltean return err; 4483dc80afcSVladimir Oltean } 4493dc80afcSVladimir Oltean 450685fb6a4SVivien Didelot static int dsa_switch_fdb_add(struct dsa_switch *ds, 451685fb6a4SVivien Didelot struct dsa_notifier_fdb_info *info) 452685fb6a4SVivien Didelot { 453726816a1SVladimir Oltean int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index); 454fac6abd5SVladimir Oltean struct dsa_port *dp = dsa_to_port(ds, port); 455685fb6a4SVivien Didelot 4561b6dd556SArkadi Sharshevsky if (!ds->ops->port_fdb_add) 457685fb6a4SVivien Didelot return -EOPNOTSUPP; 458685fb6a4SVivien Didelot 459c2693363SVladimir Oltean return dsa_port_do_fdb_add(dp, info->addr, info->vid, info->db); 460685fb6a4SVivien Didelot } 461685fb6a4SVivien Didelot 462685fb6a4SVivien Didelot static int dsa_switch_fdb_del(struct dsa_switch *ds, 463685fb6a4SVivien Didelot struct dsa_notifier_fdb_info *info) 464685fb6a4SVivien Didelot { 465726816a1SVladimir Oltean int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index); 466fac6abd5SVladimir Oltean struct dsa_port *dp = dsa_to_port(ds, port); 467685fb6a4SVivien Didelot 468685fb6a4SVivien Didelot if (!ds->ops->port_fdb_del) 469685fb6a4SVivien Didelot return -EOPNOTSUPP; 470685fb6a4SVivien Didelot 471c2693363SVladimir Oltean return dsa_port_do_fdb_del(dp, info->addr, info->vid, info->db); 472685fb6a4SVivien Didelot } 473685fb6a4SVivien Didelot 474e212fa7cSVladimir Oltean static int dsa_switch_lag_fdb_add(struct dsa_switch *ds, 475e212fa7cSVladimir Oltean struct dsa_notifier_lag_fdb_info *info) 476e212fa7cSVladimir Oltean { 477e212fa7cSVladimir Oltean struct dsa_port *dp; 478e212fa7cSVladimir Oltean 479e212fa7cSVladimir Oltean if (!ds->ops->lag_fdb_add) 480e212fa7cSVladimir Oltean return -EOPNOTSUPP; 481e212fa7cSVladimir Oltean 482e212fa7cSVladimir Oltean /* Notify switch only if it has a port in this LAG */ 483e212fa7cSVladimir Oltean dsa_switch_for_each_port(dp, ds) 484e212fa7cSVladimir Oltean if (dsa_port_offloads_lag(dp, info->lag)) 485e212fa7cSVladimir Oltean return dsa_switch_do_lag_fdb_add(ds, info->lag, 486c2693363SVladimir Oltean info->addr, info->vid, 487c2693363SVladimir Oltean info->db); 488e212fa7cSVladimir Oltean 489e212fa7cSVladimir Oltean return 0; 490e212fa7cSVladimir Oltean } 491e212fa7cSVladimir Oltean 492e212fa7cSVladimir Oltean static int dsa_switch_lag_fdb_del(struct dsa_switch *ds, 493e212fa7cSVladimir Oltean struct dsa_notifier_lag_fdb_info *info) 494e212fa7cSVladimir Oltean { 495e212fa7cSVladimir Oltean struct dsa_port *dp; 496e212fa7cSVladimir Oltean 497e212fa7cSVladimir Oltean if (!ds->ops->lag_fdb_del) 498e212fa7cSVladimir Oltean return -EOPNOTSUPP; 499e212fa7cSVladimir Oltean 500e212fa7cSVladimir Oltean /* Notify switch only if it has a port in this LAG */ 501e212fa7cSVladimir Oltean dsa_switch_for_each_port(dp, ds) 502e212fa7cSVladimir Oltean if (dsa_port_offloads_lag(dp, info->lag)) 503e212fa7cSVladimir Oltean return dsa_switch_do_lag_fdb_del(ds, info->lag, 504c2693363SVladimir Oltean info->addr, info->vid, 505c2693363SVladimir Oltean info->db); 506e212fa7cSVladimir Oltean 507e212fa7cSVladimir Oltean return 0; 508e212fa7cSVladimir Oltean } 509e212fa7cSVladimir Oltean 510058102a6STobias Waldekranz static int dsa_switch_lag_change(struct dsa_switch *ds, 511058102a6STobias Waldekranz struct dsa_notifier_lag_info *info) 512058102a6STobias Waldekranz { 513726816a1SVladimir Oltean if (info->dp->ds == ds && ds->ops->port_lag_change) 514726816a1SVladimir Oltean return ds->ops->port_lag_change(ds, info->dp->index); 515058102a6STobias Waldekranz 516726816a1SVladimir Oltean if (info->dp->ds != ds && ds->ops->crosschip_lag_change) 517726816a1SVladimir Oltean return ds->ops->crosschip_lag_change(ds, info->dp->ds->index, 518726816a1SVladimir Oltean info->dp->index); 519058102a6STobias Waldekranz 520058102a6STobias Waldekranz return 0; 521058102a6STobias Waldekranz } 522058102a6STobias Waldekranz 523058102a6STobias Waldekranz static int dsa_switch_lag_join(struct dsa_switch *ds, 524058102a6STobias Waldekranz struct dsa_notifier_lag_info *info) 525058102a6STobias Waldekranz { 526726816a1SVladimir Oltean if (info->dp->ds == ds && ds->ops->port_lag_join) 527726816a1SVladimir Oltean return ds->ops->port_lag_join(ds, info->dp->index, info->lag, 5282e359b00SVladimir Oltean info->info, info->extack); 529058102a6STobias Waldekranz 530726816a1SVladimir Oltean if (info->dp->ds != ds && ds->ops->crosschip_lag_join) 531726816a1SVladimir Oltean return ds->ops->crosschip_lag_join(ds, info->dp->ds->index, 532726816a1SVladimir Oltean info->dp->index, info->lag, 5332e359b00SVladimir Oltean info->info, info->extack); 534058102a6STobias Waldekranz 535b71d0987SVladimir Oltean return -EOPNOTSUPP; 536058102a6STobias Waldekranz } 537058102a6STobias Waldekranz 538058102a6STobias Waldekranz static int dsa_switch_lag_leave(struct dsa_switch *ds, 539058102a6STobias Waldekranz struct dsa_notifier_lag_info *info) 540058102a6STobias Waldekranz { 541726816a1SVladimir Oltean if (info->dp->ds == ds && ds->ops->port_lag_leave) 542726816a1SVladimir Oltean return ds->ops->port_lag_leave(ds, info->dp->index, info->lag); 543058102a6STobias Waldekranz 544726816a1SVladimir Oltean if (info->dp->ds != ds && ds->ops->crosschip_lag_leave) 545726816a1SVladimir Oltean return ds->ops->crosschip_lag_leave(ds, info->dp->ds->index, 546726816a1SVladimir Oltean info->dp->index, info->lag); 547058102a6STobias Waldekranz 548b71d0987SVladimir Oltean return -EOPNOTSUPP; 549058102a6STobias Waldekranz } 550058102a6STobias Waldekranz 551ffb68fc5SVladimir Oltean static int dsa_switch_mdb_add(struct dsa_switch *ds, 552e65d45ccSVivien Didelot struct dsa_notifier_mdb_info *info) 553e6db98dbSVivien Didelot { 554726816a1SVladimir Oltean int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index); 555fac6abd5SVladimir Oltean struct dsa_port *dp = dsa_to_port(ds, port); 556e6db98dbSVivien Didelot 557a52b2da7SVladimir Oltean if (!ds->ops->port_mdb_add) 558e6db98dbSVivien Didelot return -EOPNOTSUPP; 559e6db98dbSVivien Didelot 560c2693363SVladimir Oltean return dsa_port_do_mdb_add(dp, info->mdb, info->db); 561e6db98dbSVivien Didelot } 5628ae5bcdcSVivien Didelot 5638ae5bcdcSVivien Didelot static int dsa_switch_mdb_del(struct dsa_switch *ds, 5648ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info *info) 5658ae5bcdcSVivien Didelot { 566726816a1SVladimir Oltean int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index); 567fac6abd5SVladimir Oltean struct dsa_port *dp = dsa_to_port(ds, port); 568161ca59dSVladimir Oltean 5698ae5bcdcSVivien Didelot if (!ds->ops->port_mdb_del) 5708ae5bcdcSVivien Didelot return -EOPNOTSUPP; 5718ae5bcdcSVivien Didelot 572c2693363SVladimir Oltean return dsa_port_do_mdb_del(dp, info->mdb, info->db); 5738ae5bcdcSVivien Didelot } 5748ae5bcdcSVivien Didelot 575b8e997c4SVladimir Oltean static int dsa_switch_host_mdb_add(struct dsa_switch *ds, 576b8e997c4SVladimir Oltean struct dsa_notifier_mdb_info *info) 577b8e997c4SVladimir Oltean { 578fac6abd5SVladimir Oltean struct dsa_port *dp; 579b8e997c4SVladimir Oltean int err = 0; 580b8e997c4SVladimir Oltean 581b8e997c4SVladimir Oltean if (!ds->ops->port_mdb_add) 582b8e997c4SVladimir Oltean return -EOPNOTSUPP; 583b8e997c4SVladimir Oltean 584fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 585726816a1SVladimir Oltean if (dsa_port_host_address_match(dp, info->dp)) { 586c2693363SVladimir Oltean err = dsa_port_do_mdb_add(dp, info->mdb, info->db); 587b8e997c4SVladimir Oltean if (err) 588b8e997c4SVladimir Oltean break; 589b8e997c4SVladimir Oltean } 590b8e997c4SVladimir Oltean } 591b8e997c4SVladimir Oltean 592b8e997c4SVladimir Oltean return err; 593b8e997c4SVladimir Oltean } 594b8e997c4SVladimir Oltean 595b8e997c4SVladimir Oltean static int dsa_switch_host_mdb_del(struct dsa_switch *ds, 596b8e997c4SVladimir Oltean struct dsa_notifier_mdb_info *info) 597b8e997c4SVladimir Oltean { 598fac6abd5SVladimir Oltean struct dsa_port *dp; 599161ca59dSVladimir Oltean int err = 0; 600161ca59dSVladimir Oltean 601b8e997c4SVladimir Oltean if (!ds->ops->port_mdb_del) 602b8e997c4SVladimir Oltean return -EOPNOTSUPP; 603b8e997c4SVladimir Oltean 604fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 605726816a1SVladimir Oltean if (dsa_port_host_address_match(dp, info->dp)) { 606c2693363SVladimir Oltean err = dsa_port_do_mdb_del(dp, info->mdb, info->db); 607161ca59dSVladimir Oltean if (err) 608161ca59dSVladimir Oltean break; 609161ca59dSVladimir Oltean } 610161ca59dSVladimir Oltean } 611b8e997c4SVladimir Oltean 612161ca59dSVladimir Oltean return err; 613b8e997c4SVladimir Oltean } 614b8e997c4SVladimir Oltean 615134ef238SVladimir Oltean /* Port VLANs match on the targeted port and on all DSA ports */ 616fac6abd5SVladimir Oltean static bool dsa_port_vlan_match(struct dsa_port *dp, 617e65d45ccSVivien Didelot struct dsa_notifier_vlan_info *info) 618e65d45ccSVivien Didelot { 619726816a1SVladimir Oltean return dsa_port_is_dsa(dp) || dp == info->dp; 620e65d45ccSVivien Didelot } 621e65d45ccSVivien Didelot 622134ef238SVladimir Oltean /* Host VLANs match on the targeted port's CPU port, and on all DSA ports 623134ef238SVladimir Oltean * (upstream and downstream) of that switch and its upstream switches. 624134ef238SVladimir Oltean */ 625134ef238SVladimir Oltean static bool dsa_port_host_vlan_match(struct dsa_port *dp, 626726816a1SVladimir Oltean const struct dsa_port *targeted_dp) 627134ef238SVladimir Oltean { 628726816a1SVladimir Oltean struct dsa_port *cpu_dp = targeted_dp->cpu_dp; 629134ef238SVladimir Oltean 630726816a1SVladimir Oltean if (dsa_switch_is_upstream_of(dp->ds, targeted_dp->ds)) 631134ef238SVladimir Oltean return dsa_port_is_dsa(dp) || dp == cpu_dp; 632134ef238SVladimir Oltean 633134ef238SVladimir Oltean return false; 634134ef238SVladimir Oltean } 635134ef238SVladimir Oltean 636134ef238SVladimir Oltean static struct dsa_vlan *dsa_vlan_find(struct list_head *vlan_list, 637134ef238SVladimir Oltean const struct switchdev_obj_port_vlan *vlan) 638134ef238SVladimir Oltean { 639134ef238SVladimir Oltean struct dsa_vlan *v; 640134ef238SVladimir Oltean 641134ef238SVladimir Oltean list_for_each_entry(v, vlan_list, list) 642134ef238SVladimir Oltean if (v->vid == vlan->vid) 643134ef238SVladimir Oltean return v; 644134ef238SVladimir Oltean 645134ef238SVladimir Oltean return NULL; 646134ef238SVladimir Oltean } 647134ef238SVladimir Oltean 648134ef238SVladimir Oltean static int dsa_port_do_vlan_add(struct dsa_port *dp, 649134ef238SVladimir Oltean const struct switchdev_obj_port_vlan *vlan, 650134ef238SVladimir Oltean struct netlink_ext_ack *extack) 651134ef238SVladimir Oltean { 652134ef238SVladimir Oltean struct dsa_switch *ds = dp->ds; 653134ef238SVladimir Oltean int port = dp->index; 654134ef238SVladimir Oltean struct dsa_vlan *v; 655134ef238SVladimir Oltean int err = 0; 656134ef238SVladimir Oltean 657134ef238SVladimir Oltean /* No need to bother with refcounting for user ports. */ 658134ef238SVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 659134ef238SVladimir Oltean return ds->ops->port_vlan_add(ds, port, vlan, extack); 660134ef238SVladimir Oltean 661134ef238SVladimir Oltean /* No need to propagate on shared ports the existing VLANs that were 662134ef238SVladimir Oltean * re-notified after just the flags have changed. This would cause a 663134ef238SVladimir Oltean * refcount bump which we need to avoid, since it unbalances the 664134ef238SVladimir Oltean * additions with the deletions. 665134ef238SVladimir Oltean */ 666134ef238SVladimir Oltean if (vlan->changed) 667134ef238SVladimir Oltean return 0; 668134ef238SVladimir Oltean 669134ef238SVladimir Oltean mutex_lock(&dp->vlans_lock); 670134ef238SVladimir Oltean 671134ef238SVladimir Oltean v = dsa_vlan_find(&dp->vlans, vlan); 672134ef238SVladimir Oltean if (v) { 673134ef238SVladimir Oltean refcount_inc(&v->refcount); 674134ef238SVladimir Oltean goto out; 675134ef238SVladimir Oltean } 676134ef238SVladimir Oltean 677134ef238SVladimir Oltean v = kzalloc(sizeof(*v), GFP_KERNEL); 678134ef238SVladimir Oltean if (!v) { 679134ef238SVladimir Oltean err = -ENOMEM; 680134ef238SVladimir Oltean goto out; 681134ef238SVladimir Oltean } 682134ef238SVladimir Oltean 683134ef238SVladimir Oltean err = ds->ops->port_vlan_add(ds, port, vlan, extack); 684134ef238SVladimir Oltean if (err) { 685134ef238SVladimir Oltean kfree(v); 686134ef238SVladimir Oltean goto out; 687134ef238SVladimir Oltean } 688134ef238SVladimir Oltean 689134ef238SVladimir Oltean v->vid = vlan->vid; 690134ef238SVladimir Oltean refcount_set(&v->refcount, 1); 691134ef238SVladimir Oltean list_add_tail(&v->list, &dp->vlans); 692134ef238SVladimir Oltean 693134ef238SVladimir Oltean out: 694134ef238SVladimir Oltean mutex_unlock(&dp->vlans_lock); 695134ef238SVladimir Oltean 696134ef238SVladimir Oltean return err; 697134ef238SVladimir Oltean } 698134ef238SVladimir Oltean 699134ef238SVladimir Oltean static int dsa_port_do_vlan_del(struct dsa_port *dp, 700134ef238SVladimir Oltean const struct switchdev_obj_port_vlan *vlan) 701134ef238SVladimir Oltean { 702134ef238SVladimir Oltean struct dsa_switch *ds = dp->ds; 703134ef238SVladimir Oltean int port = dp->index; 704134ef238SVladimir Oltean struct dsa_vlan *v; 705134ef238SVladimir Oltean int err = 0; 706134ef238SVladimir Oltean 707134ef238SVladimir Oltean /* No need to bother with refcounting for user ports */ 708134ef238SVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) 709134ef238SVladimir Oltean return ds->ops->port_vlan_del(ds, port, vlan); 710134ef238SVladimir Oltean 711134ef238SVladimir Oltean mutex_lock(&dp->vlans_lock); 712134ef238SVladimir Oltean 713134ef238SVladimir Oltean v = dsa_vlan_find(&dp->vlans, vlan); 714134ef238SVladimir Oltean if (!v) { 715134ef238SVladimir Oltean err = -ENOENT; 716134ef238SVladimir Oltean goto out; 717134ef238SVladimir Oltean } 718134ef238SVladimir Oltean 719134ef238SVladimir Oltean if (!refcount_dec_and_test(&v->refcount)) 720134ef238SVladimir Oltean goto out; 721134ef238SVladimir Oltean 722134ef238SVladimir Oltean err = ds->ops->port_vlan_del(ds, port, vlan); 723134ef238SVladimir Oltean if (err) { 724134ef238SVladimir Oltean refcount_set(&v->refcount, 1); 725134ef238SVladimir Oltean goto out; 726134ef238SVladimir Oltean } 727134ef238SVladimir Oltean 728134ef238SVladimir Oltean list_del(&v->list); 729134ef238SVladimir Oltean kfree(v); 730134ef238SVladimir Oltean 731134ef238SVladimir Oltean out: 732134ef238SVladimir Oltean mutex_unlock(&dp->vlans_lock); 733134ef238SVladimir Oltean 734134ef238SVladimir Oltean return err; 735134ef238SVladimir Oltean } 736134ef238SVladimir Oltean 737ffb68fc5SVladimir Oltean static int dsa_switch_vlan_add(struct dsa_switch *ds, 738e65d45ccSVivien Didelot struct dsa_notifier_vlan_info *info) 7399c428c59SVivien Didelot { 740fac6abd5SVladimir Oltean struct dsa_port *dp; 741fac6abd5SVladimir Oltean int err; 7429c428c59SVivien Didelot 7431958d581SVladimir Oltean if (!ds->ops->port_vlan_add) 7449c428c59SVivien Didelot return -EOPNOTSUPP; 7459c428c59SVivien Didelot 746fac6abd5SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 747fac6abd5SVladimir Oltean if (dsa_port_vlan_match(dp, info)) { 748134ef238SVladimir Oltean err = dsa_port_do_vlan_add(dp, info->vlan, 74931046a5fSVladimir Oltean info->extack); 7509c428c59SVivien Didelot if (err) 7519c428c59SVivien Didelot return err; 7529c428c59SVivien Didelot } 753e65d45ccSVivien Didelot } 7549c428c59SVivien Didelot 755d0c627b8SVivien Didelot return 0; 756d0c627b8SVivien Didelot } 757d0c627b8SVivien Didelot 758d0c627b8SVivien Didelot static int dsa_switch_vlan_del(struct dsa_switch *ds, 759d0c627b8SVivien Didelot struct dsa_notifier_vlan_info *info) 760d0c627b8SVivien Didelot { 761134ef238SVladimir Oltean struct dsa_port *dp; 762134ef238SVladimir Oltean int err; 763134ef238SVladimir Oltean 764d0c627b8SVivien Didelot if (!ds->ops->port_vlan_del) 765d0c627b8SVivien Didelot return -EOPNOTSUPP; 766d0c627b8SVivien Didelot 767134ef238SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 768134ef238SVladimir Oltean if (dsa_port_vlan_match(dp, info)) { 769134ef238SVladimir Oltean err = dsa_port_do_vlan_del(dp, info->vlan); 770134ef238SVladimir Oltean if (err) 771134ef238SVladimir Oltean return err; 772134ef238SVladimir Oltean } 773134ef238SVladimir Oltean } 7741ca4aa9cSVivien Didelot 775134ef238SVladimir Oltean return 0; 776134ef238SVladimir Oltean } 777134ef238SVladimir Oltean 778134ef238SVladimir Oltean static int dsa_switch_host_vlan_add(struct dsa_switch *ds, 779134ef238SVladimir Oltean struct dsa_notifier_vlan_info *info) 780134ef238SVladimir Oltean { 781134ef238SVladimir Oltean struct dsa_port *dp; 782134ef238SVladimir Oltean int err; 783134ef238SVladimir Oltean 784134ef238SVladimir Oltean if (!ds->ops->port_vlan_add) 785134ef238SVladimir Oltean return -EOPNOTSUPP; 786134ef238SVladimir Oltean 787134ef238SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 788726816a1SVladimir Oltean if (dsa_port_host_vlan_match(dp, info->dp)) { 789134ef238SVladimir Oltean err = dsa_port_do_vlan_add(dp, info->vlan, 790134ef238SVladimir Oltean info->extack); 791134ef238SVladimir Oltean if (err) 792134ef238SVladimir Oltean return err; 793134ef238SVladimir Oltean } 794134ef238SVladimir Oltean } 795134ef238SVladimir Oltean 796134ef238SVladimir Oltean return 0; 797134ef238SVladimir Oltean } 798134ef238SVladimir Oltean 799134ef238SVladimir Oltean static int dsa_switch_host_vlan_del(struct dsa_switch *ds, 800134ef238SVladimir Oltean struct dsa_notifier_vlan_info *info) 801134ef238SVladimir Oltean { 802134ef238SVladimir Oltean struct dsa_port *dp; 803134ef238SVladimir Oltean int err; 804134ef238SVladimir Oltean 805134ef238SVladimir Oltean if (!ds->ops->port_vlan_del) 806134ef238SVladimir Oltean return -EOPNOTSUPP; 807134ef238SVladimir Oltean 808134ef238SVladimir Oltean dsa_switch_for_each_port(dp, ds) { 809726816a1SVladimir Oltean if (dsa_port_host_vlan_match(dp, info->dp)) { 810134ef238SVladimir Oltean err = dsa_port_do_vlan_del(dp, info->vlan); 811134ef238SVladimir Oltean if (err) 812134ef238SVladimir Oltean return err; 813134ef238SVladimir Oltean } 814134ef238SVladimir Oltean } 815134ef238SVladimir Oltean 8161ca4aa9cSVivien Didelot return 0; 817d0c627b8SVivien Didelot } 818d0c627b8SVivien Didelot 81953da0ebaSVladimir Oltean static int dsa_switch_change_tag_proto(struct dsa_switch *ds, 82053da0ebaSVladimir Oltean struct dsa_notifier_tag_proto_info *info) 82153da0ebaSVladimir Oltean { 82253da0ebaSVladimir Oltean const struct dsa_device_ops *tag_ops = info->tag_ops; 823d0004a02SVladimir Oltean struct dsa_port *dp, *cpu_dp; 824d0004a02SVladimir Oltean int err; 82553da0ebaSVladimir Oltean 82653da0ebaSVladimir Oltean if (!ds->ops->change_tag_protocol) 82753da0ebaSVladimir Oltean return -EOPNOTSUPP; 82853da0ebaSVladimir Oltean 82953da0ebaSVladimir Oltean ASSERT_RTNL(); 83053da0ebaSVladimir Oltean 831bacf93b0SVladimir Oltean err = ds->ops->change_tag_protocol(ds, tag_ops->proto); 83253da0ebaSVladimir Oltean if (err) 83353da0ebaSVladimir Oltean return err; 83453da0ebaSVladimir Oltean 835bacf93b0SVladimir Oltean dsa_switch_for_each_cpu_port(cpu_dp, ds) 836d0004a02SVladimir Oltean dsa_port_set_tag_protocol(cpu_dp, tag_ops); 83753da0ebaSVladimir Oltean 83853da0ebaSVladimir Oltean /* Now that changing the tag protocol can no longer fail, let's update 83953da0ebaSVladimir Oltean * the remaining bits which are "duplicated for faster access", and the 84053da0ebaSVladimir Oltean * bits that depend on the tagger, such as the MTU. 84153da0ebaSVladimir Oltean */ 842d0004a02SVladimir Oltean dsa_switch_for_each_user_port(dp, ds) { 843d0004a02SVladimir Oltean struct net_device *slave = dp->slave; 84453da0ebaSVladimir Oltean 84553da0ebaSVladimir Oltean dsa_slave_setup_tagger(slave); 84653da0ebaSVladimir Oltean 84753da0ebaSVladimir Oltean /* rtnl_mutex is held in dsa_tree_change_tag_proto */ 84853da0ebaSVladimir Oltean dsa_slave_change_mtu(slave, slave->mtu); 84953da0ebaSVladimir Oltean } 85053da0ebaSVladimir Oltean 85153da0ebaSVladimir Oltean return 0; 85253da0ebaSVladimir Oltean } 85353da0ebaSVladimir Oltean 8547f297314SVladimir Oltean /* We use the same cross-chip notifiers to inform both the tagger side, as well 8557f297314SVladimir Oltean * as the switch side, of connection and disconnection events. 8567f297314SVladimir Oltean * Since ds->tagger_data is owned by the tagger, it isn't a hard error if the 8577f297314SVladimir Oltean * switch side doesn't support connecting to this tagger, and therefore, the 8587f297314SVladimir Oltean * fact that we don't disconnect the tagger side doesn't constitute a memory 8597f297314SVladimir Oltean * leak: the tagger will still operate with persistent per-switch memory, just 8607f297314SVladimir Oltean * with the switch side unconnected to it. What does constitute a hard error is 8617f297314SVladimir Oltean * when the switch side supports connecting but fails. 8627f297314SVladimir Oltean */ 8637f297314SVladimir Oltean static int 8647f297314SVladimir Oltean dsa_switch_connect_tag_proto(struct dsa_switch *ds, 865dc452a47SVladimir Oltean struct dsa_notifier_tag_proto_info *info) 866dc452a47SVladimir Oltean { 867dc452a47SVladimir Oltean const struct dsa_device_ops *tag_ops = info->tag_ops; 8687f297314SVladimir Oltean int err; 8697f297314SVladimir Oltean 8707f297314SVladimir Oltean /* Notify the new tagger about the connection to this switch */ 8717f297314SVladimir Oltean if (tag_ops->connect) { 8727f297314SVladimir Oltean err = tag_ops->connect(ds); 8737f297314SVladimir Oltean if (err) 8747f297314SVladimir Oltean return err; 8757f297314SVladimir Oltean } 876dc452a47SVladimir Oltean 877dc452a47SVladimir Oltean if (!ds->ops->connect_tag_protocol) 878dc452a47SVladimir Oltean return -EOPNOTSUPP; 879dc452a47SVladimir Oltean 8807f297314SVladimir Oltean /* Notify the switch about the connection to the new tagger */ 8817f297314SVladimir Oltean err = ds->ops->connect_tag_protocol(ds, tag_ops->proto); 8827f297314SVladimir Oltean if (err) { 8837f297314SVladimir Oltean /* Revert the new tagger's connection to this tree */ 8847f297314SVladimir Oltean if (tag_ops->disconnect) 8857f297314SVladimir Oltean tag_ops->disconnect(ds); 8867f297314SVladimir Oltean return err; 8877f297314SVladimir Oltean } 8887f297314SVladimir Oltean 8897f297314SVladimir Oltean return 0; 8907f297314SVladimir Oltean } 8917f297314SVladimir Oltean 8927f297314SVladimir Oltean static int 8937f297314SVladimir Oltean dsa_switch_disconnect_tag_proto(struct dsa_switch *ds, 8947f297314SVladimir Oltean struct dsa_notifier_tag_proto_info *info) 8957f297314SVladimir Oltean { 8967f297314SVladimir Oltean const struct dsa_device_ops *tag_ops = info->tag_ops; 8977f297314SVladimir Oltean 8987f297314SVladimir Oltean /* Notify the tagger about the disconnection from this switch */ 8997f297314SVladimir Oltean if (tag_ops->disconnect && ds->tagger_data) 9007f297314SVladimir Oltean tag_ops->disconnect(ds); 9017f297314SVladimir Oltean 9027f297314SVladimir Oltean /* No need to notify the switch, since it shouldn't have any 9037f297314SVladimir Oltean * resources to tear down 9047f297314SVladimir Oltean */ 9057f297314SVladimir Oltean return 0; 906dc452a47SVladimir Oltean } 907dc452a47SVladimir Oltean 908295ab96fSVladimir Oltean static int 909295ab96fSVladimir Oltean dsa_switch_master_state_change(struct dsa_switch *ds, 910295ab96fSVladimir Oltean struct dsa_notifier_master_state_info *info) 911295ab96fSVladimir Oltean { 912295ab96fSVladimir Oltean if (!ds->ops->master_state_change) 913295ab96fSVladimir Oltean return 0; 914295ab96fSVladimir Oltean 915295ab96fSVladimir Oltean ds->ops->master_state_change(ds, info->master, info->operational); 916295ab96fSVladimir Oltean 917295ab96fSVladimir Oltean return 0; 918295ab96fSVladimir Oltean } 919295ab96fSVladimir Oltean 920f515f192SVivien Didelot static int dsa_switch_event(struct notifier_block *nb, 921f515f192SVivien Didelot unsigned long event, void *info) 922f515f192SVivien Didelot { 923f515f192SVivien Didelot struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb); 924f515f192SVivien Didelot int err; 925f515f192SVivien Didelot 926f515f192SVivien Didelot switch (event) { 9271faabf74SVivien Didelot case DSA_NOTIFIER_AGEING_TIME: 9281faabf74SVivien Didelot err = dsa_switch_ageing_time(ds, info); 9291faabf74SVivien Didelot break; 93004d3a4c6SVivien Didelot case DSA_NOTIFIER_BRIDGE_JOIN: 93104d3a4c6SVivien Didelot err = dsa_switch_bridge_join(ds, info); 93204d3a4c6SVivien Didelot break; 93304d3a4c6SVivien Didelot case DSA_NOTIFIER_BRIDGE_LEAVE: 93404d3a4c6SVivien Didelot err = dsa_switch_bridge_leave(ds, info); 93504d3a4c6SVivien Didelot break; 936685fb6a4SVivien Didelot case DSA_NOTIFIER_FDB_ADD: 937685fb6a4SVivien Didelot err = dsa_switch_fdb_add(ds, info); 938685fb6a4SVivien Didelot break; 939685fb6a4SVivien Didelot case DSA_NOTIFIER_FDB_DEL: 940685fb6a4SVivien Didelot err = dsa_switch_fdb_del(ds, info); 941685fb6a4SVivien Didelot break; 9423dc80afcSVladimir Oltean case DSA_NOTIFIER_HOST_FDB_ADD: 9433dc80afcSVladimir Oltean err = dsa_switch_host_fdb_add(ds, info); 9443dc80afcSVladimir Oltean break; 9453dc80afcSVladimir Oltean case DSA_NOTIFIER_HOST_FDB_DEL: 9463dc80afcSVladimir Oltean err = dsa_switch_host_fdb_del(ds, info); 9473dc80afcSVladimir Oltean break; 948e212fa7cSVladimir Oltean case DSA_NOTIFIER_LAG_FDB_ADD: 949e212fa7cSVladimir Oltean err = dsa_switch_lag_fdb_add(ds, info); 950e212fa7cSVladimir Oltean break; 951e212fa7cSVladimir Oltean case DSA_NOTIFIER_LAG_FDB_DEL: 952e212fa7cSVladimir Oltean err = dsa_switch_lag_fdb_del(ds, info); 953e212fa7cSVladimir Oltean break; 954058102a6STobias Waldekranz case DSA_NOTIFIER_LAG_CHANGE: 955058102a6STobias Waldekranz err = dsa_switch_lag_change(ds, info); 956058102a6STobias Waldekranz break; 957058102a6STobias Waldekranz case DSA_NOTIFIER_LAG_JOIN: 958058102a6STobias Waldekranz err = dsa_switch_lag_join(ds, info); 959058102a6STobias Waldekranz break; 960058102a6STobias Waldekranz case DSA_NOTIFIER_LAG_LEAVE: 961058102a6STobias Waldekranz err = dsa_switch_lag_leave(ds, info); 962058102a6STobias Waldekranz break; 9638ae5bcdcSVivien Didelot case DSA_NOTIFIER_MDB_ADD: 9648ae5bcdcSVivien Didelot err = dsa_switch_mdb_add(ds, info); 9658ae5bcdcSVivien Didelot break; 9668ae5bcdcSVivien Didelot case DSA_NOTIFIER_MDB_DEL: 9678ae5bcdcSVivien Didelot err = dsa_switch_mdb_del(ds, info); 9688ae5bcdcSVivien Didelot break; 969b8e997c4SVladimir Oltean case DSA_NOTIFIER_HOST_MDB_ADD: 970b8e997c4SVladimir Oltean err = dsa_switch_host_mdb_add(ds, info); 971b8e997c4SVladimir Oltean break; 972b8e997c4SVladimir Oltean case DSA_NOTIFIER_HOST_MDB_DEL: 973b8e997c4SVladimir Oltean err = dsa_switch_host_mdb_del(ds, info); 974b8e997c4SVladimir Oltean break; 975d0c627b8SVivien Didelot case DSA_NOTIFIER_VLAN_ADD: 976d0c627b8SVivien Didelot err = dsa_switch_vlan_add(ds, info); 977d0c627b8SVivien Didelot break; 978d0c627b8SVivien Didelot case DSA_NOTIFIER_VLAN_DEL: 979d0c627b8SVivien Didelot err = dsa_switch_vlan_del(ds, info); 980d0c627b8SVivien Didelot break; 981134ef238SVladimir Oltean case DSA_NOTIFIER_HOST_VLAN_ADD: 982134ef238SVladimir Oltean err = dsa_switch_host_vlan_add(ds, info); 983134ef238SVladimir Oltean break; 984134ef238SVladimir Oltean case DSA_NOTIFIER_HOST_VLAN_DEL: 985134ef238SVladimir Oltean err = dsa_switch_host_vlan_del(ds, info); 986134ef238SVladimir Oltean break; 987bfcb8132SVladimir Oltean case DSA_NOTIFIER_MTU: 988bfcb8132SVladimir Oltean err = dsa_switch_mtu(ds, info); 989bfcb8132SVladimir Oltean break; 99053da0ebaSVladimir Oltean case DSA_NOTIFIER_TAG_PROTO: 99153da0ebaSVladimir Oltean err = dsa_switch_change_tag_proto(ds, info); 99253da0ebaSVladimir Oltean break; 993dc452a47SVladimir Oltean case DSA_NOTIFIER_TAG_PROTO_CONNECT: 994dc452a47SVladimir Oltean err = dsa_switch_connect_tag_proto(ds, info); 995dc452a47SVladimir Oltean break; 9967f297314SVladimir Oltean case DSA_NOTIFIER_TAG_PROTO_DISCONNECT: 9977f297314SVladimir Oltean err = dsa_switch_disconnect_tag_proto(ds, info); 9987f297314SVladimir Oltean break; 999c64b9c05SVladimir Oltean case DSA_NOTIFIER_TAG_8021Q_VLAN_ADD: 1000c64b9c05SVladimir Oltean err = dsa_switch_tag_8021q_vlan_add(ds, info); 1001c64b9c05SVladimir Oltean break; 1002c64b9c05SVladimir Oltean case DSA_NOTIFIER_TAG_8021Q_VLAN_DEL: 1003c64b9c05SVladimir Oltean err = dsa_switch_tag_8021q_vlan_del(ds, info); 1004c64b9c05SVladimir Oltean break; 1005295ab96fSVladimir Oltean case DSA_NOTIFIER_MASTER_STATE_CHANGE: 1006295ab96fSVladimir Oltean err = dsa_switch_master_state_change(ds, info); 1007295ab96fSVladimir Oltean break; 1008f515f192SVivien Didelot default: 1009f515f192SVivien Didelot err = -EOPNOTSUPP; 1010f515f192SVivien Didelot break; 1011f515f192SVivien Didelot } 1012f515f192SVivien Didelot 1013f515f192SVivien Didelot if (err) 1014f515f192SVivien Didelot dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n", 1015f515f192SVivien Didelot event, err); 1016f515f192SVivien Didelot 1017f515f192SVivien Didelot return notifier_from_errno(err); 1018f515f192SVivien Didelot } 1019f515f192SVivien Didelot 10206dbdfce7SVladimir Oltean /** 10216dbdfce7SVladimir Oltean * dsa_tree_notify - Execute code for all switches in a DSA switch tree. 10226dbdfce7SVladimir Oltean * @dst: collection of struct dsa_switch devices to notify. 10236dbdfce7SVladimir Oltean * @e: event, must be of type DSA_NOTIFIER_* 10246dbdfce7SVladimir Oltean * @v: event-specific value. 10256dbdfce7SVladimir Oltean * 10266dbdfce7SVladimir Oltean * Given a struct dsa_switch_tree, this can be used to run a function once for 10276dbdfce7SVladimir Oltean * each member DSA switch. The other alternative of traversing the tree is only 10286dbdfce7SVladimir Oltean * through its ports list, which does not uniquely list the switches. 10296dbdfce7SVladimir Oltean */ 10306dbdfce7SVladimir Oltean int dsa_tree_notify(struct dsa_switch_tree *dst, unsigned long e, void *v) 10316dbdfce7SVladimir Oltean { 10326dbdfce7SVladimir Oltean struct raw_notifier_head *nh = &dst->nh; 10336dbdfce7SVladimir Oltean int err; 10346dbdfce7SVladimir Oltean 10356dbdfce7SVladimir Oltean err = raw_notifier_call_chain(nh, e, v); 10366dbdfce7SVladimir Oltean 10376dbdfce7SVladimir Oltean return notifier_to_errno(err); 10386dbdfce7SVladimir Oltean } 10396dbdfce7SVladimir Oltean 10406dbdfce7SVladimir Oltean /** 10416dbdfce7SVladimir Oltean * dsa_broadcast - Notify all DSA trees in the system. 10426dbdfce7SVladimir Oltean * @e: event, must be of type DSA_NOTIFIER_* 10436dbdfce7SVladimir Oltean * @v: event-specific value. 10446dbdfce7SVladimir Oltean * 10456dbdfce7SVladimir Oltean * Can be used to notify the switching fabric of events such as cross-chip 10466dbdfce7SVladimir Oltean * bridging between disjoint trees (such as islands of tagger-compatible 10476dbdfce7SVladimir Oltean * switches bridged by an incompatible middle switch). 10486dbdfce7SVladimir Oltean * 10496dbdfce7SVladimir Oltean * WARNING: this function is not reliable during probe time, because probing 10506dbdfce7SVladimir Oltean * between trees is asynchronous and not all DSA trees might have probed. 10516dbdfce7SVladimir Oltean */ 10526dbdfce7SVladimir Oltean int dsa_broadcast(unsigned long e, void *v) 10536dbdfce7SVladimir Oltean { 10546dbdfce7SVladimir Oltean struct dsa_switch_tree *dst; 10556dbdfce7SVladimir Oltean int err = 0; 10566dbdfce7SVladimir Oltean 10576dbdfce7SVladimir Oltean list_for_each_entry(dst, &dsa_tree_list, list) { 10586dbdfce7SVladimir Oltean err = dsa_tree_notify(dst, e, v); 10596dbdfce7SVladimir Oltean if (err) 10606dbdfce7SVladimir Oltean break; 10616dbdfce7SVladimir Oltean } 10626dbdfce7SVladimir Oltean 10636dbdfce7SVladimir Oltean return err; 10646dbdfce7SVladimir Oltean } 10656dbdfce7SVladimir Oltean 1066f515f192SVivien Didelot int dsa_switch_register_notifier(struct dsa_switch *ds) 1067f515f192SVivien Didelot { 1068f515f192SVivien Didelot ds->nb.notifier_call = dsa_switch_event; 1069f515f192SVivien Didelot 1070f515f192SVivien Didelot return raw_notifier_chain_register(&ds->dst->nh, &ds->nb); 1071f515f192SVivien Didelot } 1072f515f192SVivien Didelot 1073f515f192SVivien Didelot void dsa_switch_unregister_notifier(struct dsa_switch *ds) 1074f515f192SVivien Didelot { 1075f515f192SVivien Didelot int err; 1076f515f192SVivien Didelot 1077f515f192SVivien Didelot err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb); 1078f515f192SVivien Didelot if (err) 1079f515f192SVivien Didelot dev_err(ds->dev, "failed to unregister notifier (%d)\n", err); 1080f515f192SVivien Didelot } 1081