Merge tag 'dt-5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
[linux-2.6-microblaze.git] / net / dsa / switch.c
index 6466d05..bb155a1 100644 (file)
 static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds,
                                                   unsigned int ageing_time)
 {
-       int i;
-
-       for (i = 0; i < ds->num_ports; ++i) {
-               struct dsa_port *dp = dsa_to_port(ds, i);
+       struct dsa_port *dp;
 
+       dsa_switch_for_each_port(dp, ds)
                if (dp->ageing_time && dp->ageing_time < ageing_time)
                        ageing_time = dp->ageing_time;
-       }
 
        return ageing_time;
 }
@@ -49,10 +46,10 @@ static int dsa_switch_ageing_time(struct dsa_switch *ds,
        return 0;
 }
 
-static bool dsa_switch_mtu_match(struct dsa_switch *ds, int port,
-                                struct dsa_notifier_mtu_info *info)
+static bool dsa_port_mtu_match(struct dsa_port *dp,
+                              struct dsa_notifier_mtu_info *info)
 {
-       if (ds->index == info->sw_index && port == info->port)
+       if (dp->ds->index == info->sw_index && dp->index == info->port)
                return true;
 
        /* Do not propagate to other switches in the tree if the notifier was
@@ -61,7 +58,7 @@ static bool dsa_switch_mtu_match(struct dsa_switch *ds, int port,
        if (info->targeted_match)
                return false;
 
-       if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port))
+       if (dsa_port_is_dsa(dp) || dsa_port_is_cpu(dp))
                return true;
 
        return false;
@@ -70,14 +67,16 @@ static bool dsa_switch_mtu_match(struct dsa_switch *ds, int port,
 static int dsa_switch_mtu(struct dsa_switch *ds,
                          struct dsa_notifier_mtu_info *info)
 {
-       int port, ret;
+       struct dsa_port *dp;
+       int ret;
 
        if (!ds->ops->port_change_mtu)
                return -EOPNOTSUPP;
 
-       for (port = 0; port < ds->num_ports; port++) {
-               if (dsa_switch_mtu_match(ds, port, info)) {
-                       ret = ds->ops->port_change_mtu(ds, port, info->mtu);
+       dsa_switch_for_each_port(dp, ds) {
+               if (dsa_port_mtu_match(dp, info)) {
+                       ret = ds->ops->port_change_mtu(ds, dp->index,
+                                                      info->mtu);
                        if (ret)
                                return ret;
                }
@@ -120,7 +119,8 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds,
        struct netlink_ext_ack extack = {0};
        bool change_vlan_filtering = false;
        bool vlan_filtering;
-       int err, port;
+       struct dsa_port *dp;
+       int err;
 
        if (dst->index == info->tree_index && ds->index == info->sw_index &&
            ds->ops->port_bridge_leave)
@@ -150,10 +150,10 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds,
         * VLAN-aware bridge.
         */
        if (change_vlan_filtering && ds->vlan_filtering_is_global) {
-               for (port = 0; port < ds->num_ports; port++) {
+               dsa_switch_for_each_port(dp, ds) {
                        struct net_device *bridge_dev;
 
-                       bridge_dev = dsa_to_port(ds, port)->bridge_dev;
+                       bridge_dev = dp->bridge_dev;
 
                        if (bridge_dev && br_vlan_enabled(bridge_dev)) {
                                change_vlan_filtering = false;
@@ -179,19 +179,19 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds,
  * DSA links) that sit between the targeted port on which the notifier was
  * emitted and its dedicated CPU port.
  */
-static bool dsa_switch_host_address_match(struct dsa_switch *ds, int port,
-                                         int info_sw_index, int info_port)
+static bool dsa_port_host_address_match(struct dsa_port *dp,
+                                       int info_sw_index, int info_port)
 {
        struct dsa_port *targeted_dp, *cpu_dp;
        struct dsa_switch *targeted_ds;
 
-       targeted_ds = dsa_switch_find(ds->dst->index, info_sw_index);
+       targeted_ds = dsa_switch_find(dp->ds->dst->index, info_sw_index);
        targeted_dp = dsa_to_port(targeted_ds, info_port);
        cpu_dp = targeted_dp->cpu_dp;
 
-       if (dsa_switch_is_upstream_of(ds, targeted_ds))
-               return port == dsa_towards_port(ds, cpu_dp->ds->index,
-                                               cpu_dp->index);
+       if (dsa_switch_is_upstream_of(dp->ds, targeted_ds))
+               return dp->index == dsa_towards_port(dp->ds, cpu_dp->ds->index,
+                                                    cpu_dp->index);
 
        return false;
 }
@@ -209,31 +209,36 @@ static struct dsa_mac_addr *dsa_mac_addr_find(struct list_head *addr_list,
        return NULL;
 }
 
-static int dsa_switch_do_mdb_add(struct dsa_switch *ds, int port,
-                                const struct switchdev_obj_port_mdb *mdb)
+static int dsa_port_do_mdb_add(struct dsa_port *dp,
+                              const struct switchdev_obj_port_mdb *mdb)
 {
-       struct dsa_port *dp = dsa_to_port(ds, port);
+       struct dsa_switch *ds = dp->ds;
        struct dsa_mac_addr *a;
-       int err;
+       int port = dp->index;
+       int err = 0;
 
        /* No need to bother with refcounting for user ports */
        if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
                return ds->ops->port_mdb_add(ds, port, mdb);
 
+       mutex_lock(&dp->addr_lists_lock);
+
        a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid);
        if (a) {
                refcount_inc(&a->refcount);
-               return 0;
+               goto out;
        }
 
        a = kzalloc(sizeof(*a), GFP_KERNEL);
-       if (!a)
-               return -ENOMEM;
+       if (!a) {
+               err = -ENOMEM;
+               goto out;
+       }
 
        err = ds->ops->port_mdb_add(ds, port, mdb);
        if (err) {
                kfree(a);
-               return err;
+               goto out;
        }
 
        ether_addr_copy(a->addr, mdb->addr);
@@ -241,64 +246,80 @@ static int dsa_switch_do_mdb_add(struct dsa_switch *ds, int port,
        refcount_set(&a->refcount, 1);
        list_add_tail(&a->list, &dp->mdbs);
 
-       return 0;
+out:
+       mutex_unlock(&dp->addr_lists_lock);
+
+       return err;
 }
 
-static int dsa_switch_do_mdb_del(struct dsa_switch *ds, int port,
-                                const struct switchdev_obj_port_mdb *mdb)
+static int dsa_port_do_mdb_del(struct dsa_port *dp,
+                              const struct switchdev_obj_port_mdb *mdb)
 {
-       struct dsa_port *dp = dsa_to_port(ds, port);
+       struct dsa_switch *ds = dp->ds;
        struct dsa_mac_addr *a;
-       int err;
+       int port = dp->index;
+       int err = 0;
 
        /* No need to bother with refcounting for user ports */
        if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
                return ds->ops->port_mdb_del(ds, port, mdb);
 
+       mutex_lock(&dp->addr_lists_lock);
+
        a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid);
-       if (!a)
-               return -ENOENT;
+       if (!a) {
+               err = -ENOENT;
+               goto out;
+       }
 
        if (!refcount_dec_and_test(&a->refcount))
-               return 0;
+               goto out;
 
        err = ds->ops->port_mdb_del(ds, port, mdb);
        if (err) {
-               refcount_inc(&a->refcount);
-               return err;
+               refcount_set(&a->refcount, 1);
+               goto out;
        }
 
        list_del(&a->list);
        kfree(a);
 
-       return 0;
+out:
+       mutex_unlock(&dp->addr_lists_lock);
+
+       return err;
 }
 
-static int dsa_switch_do_fdb_add(struct dsa_switch *ds, int port,
-                                const unsigned char *addr, u16 vid)
+static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr,
+                              u16 vid)
 {
-       struct dsa_port *dp = dsa_to_port(ds, port);
+       struct dsa_switch *ds = dp->ds;
        struct dsa_mac_addr *a;
-       int err;
+       int port = dp->index;
+       int err = 0;
 
        /* No need to bother with refcounting for user ports */
        if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
                return ds->ops->port_fdb_add(ds, port, addr, vid);
 
+       mutex_lock(&dp->addr_lists_lock);
+
        a = dsa_mac_addr_find(&dp->fdbs, addr, vid);
        if (a) {
                refcount_inc(&a->refcount);
-               return 0;
+               goto out;
        }
 
        a = kzalloc(sizeof(*a), GFP_KERNEL);
-       if (!a)
-               return -ENOMEM;
+       if (!a) {
+               err = -ENOMEM;
+               goto out;
+       }
 
        err = ds->ops->port_fdb_add(ds, port, addr, vid);
        if (err) {
                kfree(a);
-               return err;
+               goto out;
        }
 
        ether_addr_copy(a->addr, addr);
@@ -306,53 +327,63 @@ static int dsa_switch_do_fdb_add(struct dsa_switch *ds, int port,
        refcount_set(&a->refcount, 1);
        list_add_tail(&a->list, &dp->fdbs);
 
-       return 0;
+out:
+       mutex_unlock(&dp->addr_lists_lock);
+
+       return err;
 }
 
-static int dsa_switch_do_fdb_del(struct dsa_switch *ds, int port,
-                                const unsigned char *addr, u16 vid)
+static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr,
+                              u16 vid)
 {
-       struct dsa_port *dp = dsa_to_port(ds, port);
+       struct dsa_switch *ds = dp->ds;
        struct dsa_mac_addr *a;
-       int err;
+       int port = dp->index;
+       int err = 0;
 
        /* No need to bother with refcounting for user ports */
        if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
                return ds->ops->port_fdb_del(ds, port, addr, vid);
 
+       mutex_lock(&dp->addr_lists_lock);
+
        a = dsa_mac_addr_find(&dp->fdbs, addr, vid);
-       if (!a)
-               return -ENOENT;
+       if (!a) {
+               err = -ENOENT;
+               goto out;
+       }
 
        if (!refcount_dec_and_test(&a->refcount))
-               return 0;
+               goto out;
 
        err = ds->ops->port_fdb_del(ds, port, addr, vid);
        if (err) {
-               refcount_inc(&a->refcount);
-               return err;
+               refcount_set(&a->refcount, 1);
+               goto out;
        }
 
        list_del(&a->list);
        kfree(a);
 
-       return 0;
+out:
+       mutex_unlock(&dp->addr_lists_lock);
+
+       return err;
 }
 
 static int dsa_switch_host_fdb_add(struct dsa_switch *ds,
                                   struct dsa_notifier_fdb_info *info)
 {
+       struct dsa_port *dp;
        int err = 0;
-       int port;
 
        if (!ds->ops->port_fdb_add)
                return -EOPNOTSUPP;
 
-       for (port = 0; port < ds->num_ports; port++) {
-               if (dsa_switch_host_address_match(ds, port, info->sw_index,
-                                                 info->port)) {
-                       err = dsa_switch_do_fdb_add(ds, port, info->addr,
-                                                   info->vid);
+       dsa_switch_for_each_port(dp, ds) {
+               if (dsa_port_host_address_match(dp, info->sw_index,
+                                               info->port)) {
+                       err = dsa_port_do_fdb_add(dp, info->addr, info->vid);
                        if (err)
                                break;
                }
@@ -364,17 +395,16 @@ static int dsa_switch_host_fdb_add(struct dsa_switch *ds,
 static int dsa_switch_host_fdb_del(struct dsa_switch *ds,
                                   struct dsa_notifier_fdb_info *info)
 {
+       struct dsa_port *dp;
        int err = 0;
-       int port;
 
        if (!ds->ops->port_fdb_del)
                return -EOPNOTSUPP;
 
-       for (port = 0; port < ds->num_ports; port++) {
-               if (dsa_switch_host_address_match(ds, port, info->sw_index,
-                                                 info->port)) {
-                       err = dsa_switch_do_fdb_del(ds, port, info->addr,
-                                                   info->vid);
+       dsa_switch_for_each_port(dp, ds) {
+               if (dsa_port_host_address_match(dp, info->sw_index,
+                                               info->port)) {
+                       err = dsa_port_do_fdb_del(dp, info->addr, info->vid);
                        if (err)
                                break;
                }
@@ -387,22 +417,24 @@ static int dsa_switch_fdb_add(struct dsa_switch *ds,
                              struct dsa_notifier_fdb_info *info)
 {
        int port = dsa_towards_port(ds, info->sw_index, info->port);
+       struct dsa_port *dp = dsa_to_port(ds, port);
 
        if (!ds->ops->port_fdb_add)
                return -EOPNOTSUPP;
 
-       return dsa_switch_do_fdb_add(ds, port, info->addr, info->vid);
+       return dsa_port_do_fdb_add(dp, info->addr, info->vid);
 }
 
 static int dsa_switch_fdb_del(struct dsa_switch *ds,
                              struct dsa_notifier_fdb_info *info)
 {
        int port = dsa_towards_port(ds, info->sw_index, info->port);
+       struct dsa_port *dp = dsa_to_port(ds, port);
 
        if (!ds->ops->port_fdb_del)
                return -EOPNOTSUPP;
 
-       return dsa_switch_do_fdb_del(ds, port, info->addr, info->vid);
+       return dsa_port_do_fdb_del(dp, info->addr, info->vid);
 }
 
 static int dsa_switch_hsr_join(struct dsa_switch *ds,
@@ -468,37 +500,39 @@ static int dsa_switch_mdb_add(struct dsa_switch *ds,
                              struct dsa_notifier_mdb_info *info)
 {
        int port = dsa_towards_port(ds, info->sw_index, info->port);
+       struct dsa_port *dp = dsa_to_port(ds, port);
 
        if (!ds->ops->port_mdb_add)
                return -EOPNOTSUPP;
 
-       return dsa_switch_do_mdb_add(ds, port, info->mdb);
+       return dsa_port_do_mdb_add(dp, info->mdb);
 }
 
 static int dsa_switch_mdb_del(struct dsa_switch *ds,
                              struct dsa_notifier_mdb_info *info)
 {
        int port = dsa_towards_port(ds, info->sw_index, info->port);
+       struct dsa_port *dp = dsa_to_port(ds, port);
 
        if (!ds->ops->port_mdb_del)
                return -EOPNOTSUPP;
 
-       return dsa_switch_do_mdb_del(ds, port, info->mdb);
+       return dsa_port_do_mdb_del(dp, info->mdb);
 }
 
 static int dsa_switch_host_mdb_add(struct dsa_switch *ds,
                                   struct dsa_notifier_mdb_info *info)
 {
+       struct dsa_port *dp;
        int err = 0;
-       int port;
 
        if (!ds->ops->port_mdb_add)
                return -EOPNOTSUPP;
 
-       for (port = 0; port < ds->num_ports; port++) {
-               if (dsa_switch_host_address_match(ds, port, info->sw_index,
-                                                 info->port)) {
-                       err = dsa_switch_do_mdb_add(ds, port, info->mdb);
+       dsa_switch_for_each_port(dp, ds) {
+               if (dsa_port_host_address_match(dp, info->sw_index,
+                                               info->port)) {
+                       err = dsa_port_do_mdb_add(dp, info->mdb);
                        if (err)
                                break;
                }
@@ -510,16 +544,16 @@ static int dsa_switch_host_mdb_add(struct dsa_switch *ds,
 static int dsa_switch_host_mdb_del(struct dsa_switch *ds,
                                   struct dsa_notifier_mdb_info *info)
 {
+       struct dsa_port *dp;
        int err = 0;
-       int port;
 
        if (!ds->ops->port_mdb_del)
                return -EOPNOTSUPP;
 
-       for (port = 0; port < ds->num_ports; port++) {
-               if (dsa_switch_host_address_match(ds, port, info->sw_index,
-                                                 info->port)) {
-                       err = dsa_switch_do_mdb_del(ds, port, info->mdb);
+       dsa_switch_for_each_port(dp, ds) {
+               if (dsa_port_host_address_match(dp, info->sw_index,
+                                               info->port)) {
+                       err = dsa_port_do_mdb_del(dp, info->mdb);
                        if (err)
                                break;
                }
@@ -528,13 +562,13 @@ static int dsa_switch_host_mdb_del(struct dsa_switch *ds,
        return err;
 }
 
-static bool dsa_switch_vlan_match(struct dsa_switch *ds, int port,
-                                 struct dsa_notifier_vlan_info *info)
+static bool dsa_port_vlan_match(struct dsa_port *dp,
+                               struct dsa_notifier_vlan_info *info)
 {
-       if (ds->index == info->sw_index && port == info->port)
+       if (dp->ds->index == info->sw_index && dp->index == info->port)
                return true;
 
-       if (dsa_is_dsa_port(ds, port))
+       if (dsa_port_is_dsa(dp))
                return true;
 
        return false;
@@ -543,14 +577,15 @@ static bool dsa_switch_vlan_match(struct dsa_switch *ds, int port,
 static int dsa_switch_vlan_add(struct dsa_switch *ds,
                               struct dsa_notifier_vlan_info *info)
 {
-       int port, err;
+       struct dsa_port *dp;
+       int err;
 
        if (!ds->ops->port_vlan_add)
                return -EOPNOTSUPP;
 
-       for (port = 0; port < ds->num_ports; port++) {
-               if (dsa_switch_vlan_match(ds, port, info)) {
-                       err = ds->ops->port_vlan_add(ds, port, info->vlan,
+       dsa_switch_for_each_port(dp, ds) {
+               if (dsa_port_vlan_match(dp, info)) {
+                       err = ds->ops->port_vlan_add(ds, dp->index, info->vlan,
                                                     info->extack);
                        if (err)
                                return err;
@@ -579,38 +614,34 @@ static int dsa_switch_change_tag_proto(struct dsa_switch *ds,
                                       struct dsa_notifier_tag_proto_info *info)
 {
        const struct dsa_device_ops *tag_ops = info->tag_ops;
-       int port, err;
+       struct dsa_port *dp, *cpu_dp;
+       int err;
 
        if (!ds->ops->change_tag_protocol)
                return -EOPNOTSUPP;
 
        ASSERT_RTNL();
 
-       for (port = 0; port < ds->num_ports; port++) {
-               if (!dsa_is_cpu_port(ds, port))
-                       continue;
-
-               err = ds->ops->change_tag_protocol(ds, port, tag_ops->proto);
+       dsa_switch_for_each_cpu_port(cpu_dp, ds) {
+               err = ds->ops->change_tag_protocol(ds, cpu_dp->index,
+                                                  tag_ops->proto);
                if (err)
                        return err;
 
-               dsa_port_set_tag_protocol(dsa_to_port(ds, port), tag_ops);
+               dsa_port_set_tag_protocol(cpu_dp, tag_ops);
        }
 
        /* Now that changing the tag protocol can no longer fail, let's update
         * the remaining bits which are "duplicated for faster access", and the
         * bits that depend on the tagger, such as the MTU.
         */
-       for (port = 0; port < ds->num_ports; port++) {
-               if (dsa_is_user_port(ds, port)) {
-                       struct net_device *slave;
+       dsa_switch_for_each_user_port(dp, ds) {
+               struct net_device *slave = dp->slave;
 
-                       slave = dsa_to_port(ds, port)->slave;
-                       dsa_slave_setup_tagger(slave);
+               dsa_slave_setup_tagger(slave);
 
-                       /* rtnl_mutex is held in dsa_tree_change_tag_proto */
-                       dsa_slave_change_mtu(slave, slave->mtu);
-               }
+               /* rtnl_mutex is held in dsa_tree_change_tag_proto */
+               dsa_slave_change_mtu(slave, slave->mtu);
        }
 
        return 0;