Merge branch 'dsa-port-fast-ageing'
[cascardo/linux.git] / net / dsa / slave.c
index fc91967..6b1282c 100644 (file)
@@ -28,7 +28,7 @@ static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg)
        struct dsa_switch *ds = bus->priv;
 
        if (ds->phys_mii_mask & (1 << addr))
-               return ds->drv->phy_read(ds, addr, reg);
+               return ds->ops->phy_read(ds, addr, reg);
 
        return 0xffff;
 }
@@ -38,7 +38,7 @@ static int dsa_slave_phy_write(struct mii_bus *bus, int addr, int reg, u16 val)
        struct dsa_switch *ds = bus->priv;
 
        if (ds->phys_mii_mask & (1 << addr))
-               return ds->drv->phy_write(ds, addr, reg, val);
+               return ds->ops->phy_write(ds, addr, reg, val);
 
        return 0;
 }
@@ -69,6 +69,30 @@ static inline bool dsa_port_is_bridged(struct dsa_slave_priv *p)
        return !!p->bridge_dev;
 }
 
+static void dsa_port_set_stp_state(struct dsa_switch *ds, int port, u8 state)
+{
+       struct dsa_port *dp = &ds->ports[port];
+
+       if (ds->ops->port_stp_state_set)
+               ds->ops->port_stp_state_set(ds, port, state);
+
+       if (ds->ops->port_fast_age) {
+               /* Fast age FDB entries or flush appropriate forwarding database
+                * for the given port, if we are moving it from Learning or
+                * Forwarding state, to Disabled or Blocking or Listening state.
+                */
+
+               if ((dp->stp_state == BR_STATE_LEARNING ||
+                    dp->stp_state == BR_STATE_FORWARDING) &&
+                   (state == BR_STATE_DISABLED ||
+                    state == BR_STATE_BLOCKING ||
+                    state == BR_STATE_LISTENING))
+                       ds->ops->port_fast_age(ds, port);
+       }
+
+       dp->stp_state = state;
+}
+
 static int dsa_slave_open(struct net_device *dev)
 {
        struct dsa_slave_priv *p = netdev_priv(dev);
@@ -98,14 +122,13 @@ static int dsa_slave_open(struct net_device *dev)
                        goto clear_allmulti;
        }
 
-       if (ds->drv->port_enable) {
-               err = ds->drv->port_enable(ds, p->port, p->phy);
+       if (ds->ops->port_enable) {
+               err = ds->ops->port_enable(ds, p->port, p->phy);
                if (err)
                        goto clear_promisc;
        }
 
-       if (ds->drv->port_stp_state_set)
-               ds->drv->port_stp_state_set(ds, p->port, stp_state);
+       dsa_port_set_stp_state(ds, p->port, stp_state);
 
        if (p->phy)
                phy_start(p->phy);
@@ -144,11 +167,10 @@ static int dsa_slave_close(struct net_device *dev)
        if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
                dev_uc_del(master, dev->dev_addr);
 
-       if (ds->drv->port_disable)
-               ds->drv->port_disable(ds, p->port, p->phy);
+       if (ds->ops->port_disable)
+               ds->ops->port_disable(ds, p->port, p->phy);
 
-       if (ds->drv->port_stp_state_set)
-               ds->drv->port_stp_state_set(ds, p->port, BR_STATE_DISABLED);
+       dsa_port_set_stp_state(ds, p->port, BR_STATE_DISABLED);
 
        return 0;
 }
@@ -209,13 +231,13 @@ static int dsa_slave_port_vlan_add(struct net_device *dev,
        struct dsa_switch *ds = p->parent;
 
        if (switchdev_trans_ph_prepare(trans)) {
-               if (!ds->drv->port_vlan_prepare || !ds->drv->port_vlan_add)
+               if (!ds->ops->port_vlan_prepare || !ds->ops->port_vlan_add)
                        return -EOPNOTSUPP;
 
-               return ds->drv->port_vlan_prepare(ds, p->port, vlan, trans);
+               return ds->ops->port_vlan_prepare(ds, p->port, vlan, trans);
        }
 
-       ds->drv->port_vlan_add(ds, p->port, vlan, trans);
+       ds->ops->port_vlan_add(ds, p->port, vlan, trans);
 
        return 0;
 }
@@ -226,10 +248,10 @@ static int dsa_slave_port_vlan_del(struct net_device *dev,
        struct dsa_slave_priv *p = netdev_priv(dev);
        struct dsa_switch *ds = p->parent;
 
-       if (!ds->drv->port_vlan_del)
+       if (!ds->ops->port_vlan_del)
                return -EOPNOTSUPP;
 
-       return ds->drv->port_vlan_del(ds, p->port, vlan);
+       return ds->ops->port_vlan_del(ds, p->port, vlan);
 }
 
 static int dsa_slave_port_vlan_dump(struct net_device *dev,
@@ -239,8 +261,8 @@ static int dsa_slave_port_vlan_dump(struct net_device *dev,
        struct dsa_slave_priv *p = netdev_priv(dev);
        struct dsa_switch *ds = p->parent;
 
-       if (ds->drv->port_vlan_dump)
-               return ds->drv->port_vlan_dump(ds, p->port, vlan, cb);
+       if (ds->ops->port_vlan_dump)
+               return ds->ops->port_vlan_dump(ds, p->port, vlan, cb);
 
        return -EOPNOTSUPP;
 }
@@ -253,13 +275,13 @@ static int dsa_slave_port_fdb_add(struct net_device *dev,
        struct dsa_switch *ds = p->parent;
 
        if (switchdev_trans_ph_prepare(trans)) {
-               if (!ds->drv->port_fdb_prepare || !ds->drv->port_fdb_add)
+               if (!ds->ops->port_fdb_prepare || !ds->ops->port_fdb_add)
                        return -EOPNOTSUPP;
 
-               return ds->drv->port_fdb_prepare(ds, p->port, fdb, trans);
+               return ds->ops->port_fdb_prepare(ds, p->port, fdb, trans);
        }
 
-       ds->drv->port_fdb_add(ds, p->port, fdb, trans);
+       ds->ops->port_fdb_add(ds, p->port, fdb, trans);
 
        return 0;
 }
@@ -271,8 +293,8 @@ static int dsa_slave_port_fdb_del(struct net_device *dev,
        struct dsa_switch *ds = p->parent;
        int ret = -EOPNOTSUPP;
 
-       if (ds->drv->port_fdb_del)
-               ret = ds->drv->port_fdb_del(ds, p->port, fdb);
+       if (ds->ops->port_fdb_del)
+               ret = ds->ops->port_fdb_del(ds, p->port, fdb);
 
        return ret;
 }
@@ -284,8 +306,52 @@ static int dsa_slave_port_fdb_dump(struct net_device *dev,
        struct dsa_slave_priv *p = netdev_priv(dev);
        struct dsa_switch *ds = p->parent;
 
-       if (ds->drv->port_fdb_dump)
-               return ds->drv->port_fdb_dump(ds, p->port, fdb, cb);
+       if (ds->ops->port_fdb_dump)
+               return ds->ops->port_fdb_dump(ds, p->port, fdb, cb);
+
+       return -EOPNOTSUPP;
+}
+
+static int dsa_slave_port_mdb_add(struct net_device *dev,
+                                 const struct switchdev_obj_port_mdb *mdb,
+                                 struct switchdev_trans *trans)
+{
+       struct dsa_slave_priv *p = netdev_priv(dev);
+       struct dsa_switch *ds = p->parent;
+
+       if (switchdev_trans_ph_prepare(trans)) {
+               if (!ds->ops->port_mdb_prepare || !ds->ops->port_mdb_add)
+                       return -EOPNOTSUPP;
+
+               return ds->ops->port_mdb_prepare(ds, p->port, mdb, trans);
+       }
+
+       ds->ops->port_mdb_add(ds, p->port, mdb, trans);
+
+       return 0;
+}
+
+static int dsa_slave_port_mdb_del(struct net_device *dev,
+                                 const struct switchdev_obj_port_mdb *mdb)
+{
+       struct dsa_slave_priv *p = netdev_priv(dev);
+       struct dsa_switch *ds = p->parent;
+
+       if (ds->ops->port_mdb_del)
+               return ds->ops->port_mdb_del(ds, p->port, mdb);
+
+       return -EOPNOTSUPP;
+}
+
+static int dsa_slave_port_mdb_dump(struct net_device *dev,
+                                  struct switchdev_obj_port_mdb *mdb,
+                                  switchdev_obj_dump_cb_t *cb)
+{
+       struct dsa_slave_priv *p = netdev_priv(dev);
+       struct dsa_switch *ds = p->parent;
+
+       if (ds->ops->port_mdb_dump)
+               return ds->ops->port_mdb_dump(ds, p->port, mdb, cb);
 
        return -EOPNOTSUPP;
 }
@@ -308,9 +374,9 @@ static int dsa_slave_stp_state_set(struct net_device *dev,
        struct dsa_switch *ds = p->parent;
 
        if (switchdev_trans_ph_prepare(trans))
-               return ds->drv->port_stp_state_set ? 0 : -EOPNOTSUPP;
+               return ds->ops->port_stp_state_set ? 0 : -EOPNOTSUPP;
 
-       ds->drv->port_stp_state_set(ds, p->port, attr->u.stp_state);
+       dsa_port_set_stp_state(ds, p->port, attr->u.stp_state);
 
        return 0;
 }
@@ -326,8 +392,8 @@ static int dsa_slave_vlan_filtering(struct net_device *dev,
        if (switchdev_trans_ph_prepare(trans))
                return 0;
 
-       if (ds->drv->port_vlan_filtering)
-               return ds->drv->port_vlan_filtering(ds, p->port,
+       if (ds->ops->port_vlan_filtering)
+               return ds->ops->port_vlan_filtering(ds, p->port,
                                                    attr->u.vlan_filtering);
 
        return 0;
@@ -365,8 +431,8 @@ static int dsa_slave_ageing_time(struct net_device *dev,
        ds->ports[p->port].ageing_time = ageing_time;
        ageing_time = dsa_fastest_ageing_time(ds, ageing_time);
 
-       if (ds->drv->set_ageing_time)
-               return ds->drv->set_ageing_time(ds, ageing_time);
+       if (ds->ops->set_ageing_time)
+               return ds->ops->set_ageing_time(ds, ageing_time);
 
        return 0;
 }
@@ -412,6 +478,10 @@ static int dsa_slave_port_obj_add(struct net_device *dev,
                                             SWITCHDEV_OBJ_PORT_FDB(obj),
                                             trans);
                break;
+       case SWITCHDEV_OBJ_ID_PORT_MDB:
+               err = dsa_slave_port_mdb_add(dev, SWITCHDEV_OBJ_PORT_MDB(obj),
+                                            trans);
+               break;
        case SWITCHDEV_OBJ_ID_PORT_VLAN:
                err = dsa_slave_port_vlan_add(dev,
                                              SWITCHDEV_OBJ_PORT_VLAN(obj),
@@ -435,6 +505,9 @@ static int dsa_slave_port_obj_del(struct net_device *dev,
                err = dsa_slave_port_fdb_del(dev,
                                             SWITCHDEV_OBJ_PORT_FDB(obj));
                break;
+       case SWITCHDEV_OBJ_ID_PORT_MDB:
+               err = dsa_slave_port_mdb_del(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
+               break;
        case SWITCHDEV_OBJ_ID_PORT_VLAN:
                err = dsa_slave_port_vlan_del(dev,
                                              SWITCHDEV_OBJ_PORT_VLAN(obj));
@@ -459,6 +532,10 @@ static int dsa_slave_port_obj_dump(struct net_device *dev,
                                              SWITCHDEV_OBJ_PORT_FDB(obj),
                                              cb);
                break;
+       case SWITCHDEV_OBJ_ID_PORT_MDB:
+               err = dsa_slave_port_mdb_dump(dev, SWITCHDEV_OBJ_PORT_MDB(obj),
+                                             cb);
+               break;
        case SWITCHDEV_OBJ_ID_PORT_VLAN:
                err = dsa_slave_port_vlan_dump(dev,
                                               SWITCHDEV_OBJ_PORT_VLAN(obj),
@@ -481,8 +558,8 @@ static int dsa_slave_bridge_port_join(struct net_device *dev,
 
        p->bridge_dev = br;
 
-       if (ds->drv->port_bridge_join)
-               ret = ds->drv->port_bridge_join(ds, p->port, br);
+       if (ds->ops->port_bridge_join)
+               ret = ds->ops->port_bridge_join(ds, p->port, br);
 
        return ret == -EOPNOTSUPP ? 0 : ret;
 }
@@ -493,16 +570,15 @@ static void dsa_slave_bridge_port_leave(struct net_device *dev)
        struct dsa_switch *ds = p->parent;
 
 
-       if (ds->drv->port_bridge_leave)
-               ds->drv->port_bridge_leave(ds, p->port);
+       if (ds->ops->port_bridge_leave)
+               ds->ops->port_bridge_leave(ds, p->port);
 
        p->bridge_dev = NULL;
 
        /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
         * so allow it to be in BR_STATE_FORWARDING to be kept functional
         */
-       if (ds->drv->port_stp_state_set)
-               ds->drv->port_stp_state_set(ds, p->port, BR_STATE_FORWARDING);
+       dsa_port_set_stp_state(ds, p->port, BR_STATE_FORWARDING);
 }
 
 static int dsa_slave_port_attr_get(struct net_device *dev,
@@ -605,8 +681,8 @@ static int dsa_slave_get_regs_len(struct net_device *dev)
        struct dsa_slave_priv *p = netdev_priv(dev);
        struct dsa_switch *ds = p->parent;
 
-       if (ds->drv->get_regs_len)
-               return ds->drv->get_regs_len(ds, p->port);
+       if (ds->ops->get_regs_len)
+               return ds->ops->get_regs_len(ds, p->port);
 
        return -EOPNOTSUPP;
 }
@@ -617,8 +693,8 @@ dsa_slave_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *_p)
        struct dsa_slave_priv *p = netdev_priv(dev);
        struct dsa_switch *ds = p->parent;
 
-       if (ds->drv->get_regs)
-               ds->drv->get_regs(ds, p->port, regs, _p);
+       if (ds->ops->get_regs)
+               ds->ops->get_regs(ds, p->port, regs, _p);
 }
 
 static int dsa_slave_nway_reset(struct net_device *dev)
@@ -651,8 +727,8 @@ static int dsa_slave_get_eeprom_len(struct net_device *dev)
        if (ds->cd && ds->cd->eeprom_len)
                return ds->cd->eeprom_len;
 
-       if (ds->drv->get_eeprom_len)
-               return ds->drv->get_eeprom_len(ds);
+       if (ds->ops->get_eeprom_len)
+               return ds->ops->get_eeprom_len(ds);
 
        return 0;
 }
@@ -663,8 +739,8 @@ static int dsa_slave_get_eeprom(struct net_device *dev,
        struct dsa_slave_priv *p = netdev_priv(dev);
        struct dsa_switch *ds = p->parent;
 
-       if (ds->drv->get_eeprom)
-               return ds->drv->get_eeprom(ds, eeprom, data);
+       if (ds->ops->get_eeprom)
+               return ds->ops->get_eeprom(ds, eeprom, data);
 
        return -EOPNOTSUPP;
 }
@@ -675,8 +751,8 @@ static int dsa_slave_set_eeprom(struct net_device *dev,
        struct dsa_slave_priv *p = netdev_priv(dev);
        struct dsa_switch *ds = p->parent;
 
-       if (ds->drv->set_eeprom)
-               return ds->drv->set_eeprom(ds, eeprom, data);
+       if (ds->ops->set_eeprom)
+               return ds->ops->set_eeprom(ds, eeprom, data);
 
        return -EOPNOTSUPP;
 }
@@ -694,8 +770,8 @@ static void dsa_slave_get_strings(struct net_device *dev,
                strncpy(data + len, "tx_bytes", len);
                strncpy(data + 2 * len, "rx_packets", len);
                strncpy(data + 3 * len, "rx_bytes", len);
-               if (ds->drv->get_strings != NULL)
-                       ds->drv->get_strings(ds, p->port, data + 4 * len);
+               if (ds->ops->get_strings)
+                       ds->ops->get_strings(ds, p->port, data + 4 * len);
        }
 }
 
@@ -714,8 +790,8 @@ static void dsa_cpu_port_get_ethtool_stats(struct net_device *dev,
                dst->master_ethtool_ops.get_ethtool_stats(dev, stats, data);
        }
 
-       if (ds->drv->get_ethtool_stats)
-               ds->drv->get_ethtool_stats(ds, cpu_port, data + count);
+       if (ds->ops->get_ethtool_stats)
+               ds->ops->get_ethtool_stats(ds, cpu_port, data + count);
 }
 
 static int dsa_cpu_port_get_sset_count(struct net_device *dev, int sset)
@@ -727,8 +803,8 @@ static int dsa_cpu_port_get_sset_count(struct net_device *dev, int sset)
        if (dst->master_ethtool_ops.get_sset_count)
                count += dst->master_ethtool_ops.get_sset_count(dev, sset);
 
-       if (sset == ETH_SS_STATS && ds->drv->get_sset_count)
-               count += ds->drv->get_sset_count(ds);
+       if (sset == ETH_SS_STATS && ds->ops->get_sset_count)
+               count += ds->ops->get_sset_count(ds);
 
        return count;
 }
@@ -755,14 +831,14 @@ static void dsa_cpu_port_get_strings(struct net_device *dev,
                dst->master_ethtool_ops.get_strings(dev, stringset, data);
        }
 
-       if (stringset == ETH_SS_STATS && ds->drv->get_strings) {
+       if (stringset == ETH_SS_STATS && ds->ops->get_strings) {
                ndata = data + mcount * len;
                /* This function copies ETH_GSTRINGS_LEN bytes, we will mangle
                 * the output after to prepend our CPU port prefix we
                 * constructed earlier
                 */
-               ds->drv->get_strings(ds, cpu_port, ndata);
-               count = ds->drv->get_sset_count(ds);
+               ds->ops->get_strings(ds, cpu_port, ndata);
+               count = ds->ops->get_sset_count(ds);
                for (i = 0; i < count; i++) {
                        memmove(ndata + (i * len + sizeof(pfx)),
                                ndata + i * len, len - sizeof(pfx));
@@ -782,8 +858,8 @@ static void dsa_slave_get_ethtool_stats(struct net_device *dev,
        data[1] = dev->stats.tx_bytes;
        data[2] = dev->stats.rx_packets;
        data[3] = dev->stats.rx_bytes;
-       if (ds->drv->get_ethtool_stats != NULL)
-               ds->drv->get_ethtool_stats(ds, p->port, data + 4);
+       if (ds->ops->get_ethtool_stats)
+               ds->ops->get_ethtool_stats(ds, p->port, data + 4);
 }
 
 static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
@@ -795,8 +871,8 @@ static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
                int count;
 
                count = 4;
-               if (ds->drv->get_sset_count != NULL)
-                       count += ds->drv->get_sset_count(ds);
+               if (ds->ops->get_sset_count)
+                       count += ds->ops->get_sset_count(ds);
 
                return count;
        }
@@ -809,8 +885,8 @@ static void dsa_slave_get_wol(struct net_device *dev, struct ethtool_wolinfo *w)
        struct dsa_slave_priv *p = netdev_priv(dev);
        struct dsa_switch *ds = p->parent;
 
-       if (ds->drv->get_wol)
-               ds->drv->get_wol(ds, p->port, w);
+       if (ds->ops->get_wol)
+               ds->ops->get_wol(ds, p->port, w);
 }
 
 static int dsa_slave_set_wol(struct net_device *dev, struct ethtool_wolinfo *w)
@@ -819,8 +895,8 @@ static int dsa_slave_set_wol(struct net_device *dev, struct ethtool_wolinfo *w)
        struct dsa_switch *ds = p->parent;
        int ret = -EOPNOTSUPP;
 
-       if (ds->drv->set_wol)
-               ret = ds->drv->set_wol(ds, p->port, w);
+       if (ds->ops->set_wol)
+               ret = ds->ops->set_wol(ds, p->port, w);
 
        return ret;
 }
@@ -831,10 +907,10 @@ static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e)
        struct dsa_switch *ds = p->parent;
        int ret;
 
-       if (!ds->drv->set_eee)
+       if (!ds->ops->set_eee)
                return -EOPNOTSUPP;
 
-       ret = ds->drv->set_eee(ds, p->port, p->phy, e);
+       ret = ds->ops->set_eee(ds, p->port, p->phy, e);
        if (ret)
                return ret;
 
@@ -850,10 +926,10 @@ static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e)
        struct dsa_switch *ds = p->parent;
        int ret;
 
-       if (!ds->drv->get_eee)
+       if (!ds->ops->get_eee)
                return -EOPNOTSUPP;
 
-       ret = ds->drv->get_eee(ds, p->port, e);
+       ret = ds->ops->get_eee(ds, p->port, e);
        if (ret)
                return ret;
 
@@ -988,8 +1064,8 @@ static void dsa_slave_adjust_link(struct net_device *dev)
                p->old_pause = p->phy->pause;
        }
 
-       if (ds->drv->adjust_link && status_changed)
-               ds->drv->adjust_link(ds, p->port, p->phy);
+       if (ds->ops->adjust_link && status_changed)
+               ds->ops->adjust_link(ds, p->port, p->phy);
 
        if (status_changed)
                phy_print_status(p->phy);
@@ -1004,8 +1080,8 @@ static int dsa_slave_fixed_link_update(struct net_device *dev,
        if (dev) {
                p = netdev_priv(dev);
                ds = p->parent;
-               if (ds->drv->fixed_link_update)
-                       ds->drv->fixed_link_update(ds, p->port, status);
+               if (ds->ops->fixed_link_update)
+                       ds->ops->fixed_link_update(ds, p->port, status);
        }
 
        return 0;
@@ -1062,8 +1138,8 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
                phy_dn = port_dn;
        }
 
-       if (ds->drv->get_phy_flags)
-               phy_flags = ds->drv->get_phy_flags(ds, p->port);
+       if (ds->ops->get_phy_flags)
+               phy_flags = ds->ops->get_phy_flags(ds, p->port);
 
        if (phy_dn) {
                int phy_id = of_mdio_parse_addr(&slave_dev->dev, phy_dn);