Merge branch 'dsa-mdb-support'
authorDavid S. Miller <davem@davemloft.net>
Wed, 31 Aug 2016 21:15:43 +0000 (14:15 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 31 Aug 2016 21:15:43 +0000 (14:15 -0700)
Vivien Didelot says:

====================
net: dsa: add MDB support

This patchset adds the switchdev MDB object support to the DSA layer.

The MDB support for the mv88e6xxx driver is very similar to the FDB
support. The FDB operations care about unicast addresses while the MDB
operations care about multicast addresses.

Both operation set load/purge/dump the Address Translation Table (ATU),
thus common code is used.

Changes in v2 based on Andrew's comments:
  - drop "group" in multicast database related doc and comment
  - change _one for more relevant _fid in mv88e6xxx_port_db_dump_one
  - return -EOPNOTSUPP if switchdev obj ID is neither _FDB nor _MDB
====================

Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
Documentation/networking/dsa/dsa.txt
drivers/net/dsa/mv88e6xxx/chip.c
include/net/dsa.h
net/dsa/slave.c

index a4e55c7..6d6c07c 100644 (file)
@@ -584,6 +584,29 @@ of DSA, would be the its port-based VLAN, used by the associated bridge device.
   function that the driver has to call for each MAC address known to be behind
   the given port. A switchdev object is used to carry the VID and FDB info.
 
+- port_mdb_prepare: bridge layer function invoked when the bridge prepares the
+  installation of a multicast database entry. If the operation is not supported,
+  this function should return -EOPNOTSUPP to inform the bridge code to fallback
+  to a software implementation. No hardware setup must be done in this function.
+  See port_fdb_add for this and details.
+
+- port_mdb_add: bridge layer function invoked when the bridge wants to install
+  a multicast database entry, the switch hardware should be programmed with the
+  specified address in the specified VLAN ID in the forwarding database
+  associated with this VLAN ID.
+
+Note: VLAN ID 0 corresponds to the port private database, which, in the context
+of DSA, would be the its port-based VLAN, used by the associated bridge device.
+
+- port_mdb_del: bridge layer function invoked when the bridge wants to remove a
+  multicast database entry, the switch hardware should be programmed to delete
+  the specified MAC address from the specified VLAN ID if it was mapped into
+  this port forwarding database.
+
+- port_mdb_dump: bridge layer function invoked with a switchdev callback
+  function that the driver has to call for each MAC address known to be behind
+  the given port. A switchdev object is used to carry the VID and MDB info.
+
 TODO
 ====
 
index 4e697ee..e2a953e 100644 (file)
@@ -2093,9 +2093,9 @@ static int _mv88e6xxx_atu_load(struct mv88e6xxx_chip *chip,
        return _mv88e6xxx_atu_cmd(chip, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
 }
 
-static int _mv88e6xxx_port_fdb_load(struct mv88e6xxx_chip *chip, int port,
-                                   const unsigned char *addr, u16 vid,
-                                   u8 state)
+static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
+                                       const unsigned char *addr, u16 vid,
+                                       u8 state)
 {
        struct mv88e6xxx_atu_entry entry = { 0 };
        struct mv88e6xxx_vtu_stu_entry vlan;
@@ -2134,15 +2134,12 @@ static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
                                   const struct switchdev_obj_port_fdb *fdb,
                                   struct switchdev_trans *trans)
 {
-       int state = is_multicast_ether_addr(fdb->addr) ?
-               GLOBAL_ATU_DATA_STATE_MC_STATIC :
-               GLOBAL_ATU_DATA_STATE_UC_STATIC;
        struct mv88e6xxx_chip *chip = ds_to_priv(ds);
 
        mutex_lock(&chip->reg_lock);
-       if (_mv88e6xxx_port_fdb_load(chip, port, fdb->addr, fdb->vid, state))
-               netdev_err(ds->ports[port].netdev,
-                          "failed to load MAC address\n");
+       if (mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid,
+                                        GLOBAL_ATU_DATA_STATE_UC_STATIC))
+               netdev_err(ds->ports[port].netdev, "failed to load unicast MAC address\n");
        mutex_unlock(&chip->reg_lock);
 }
 
@@ -2150,14 +2147,14 @@ static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
                                  const struct switchdev_obj_port_fdb *fdb)
 {
        struct mv88e6xxx_chip *chip = ds_to_priv(ds);
-       int ret;
+       int err;
 
        mutex_lock(&chip->reg_lock);
-       ret = _mv88e6xxx_port_fdb_load(chip, port, fdb->addr, fdb->vid,
-                                      GLOBAL_ATU_DATA_STATE_UNUSED);
+       err = mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid,
+                                          GLOBAL_ATU_DATA_STATE_UNUSED);
        mutex_unlock(&chip->reg_lock);
 
-       return ret;
+       return err;
 }
 
 static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
@@ -2205,10 +2202,10 @@ static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
        return 0;
 }
 
-static int _mv88e6xxx_port_fdb_dump_one(struct mv88e6xxx_chip *chip,
-                                       u16 fid, u16 vid, int port,
-                                       struct switchdev_obj_port_fdb *fdb,
-                                       int (*cb)(struct switchdev_obj *obj))
+static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
+                                     u16 fid, u16 vid, int port,
+                                     struct switchdev_obj *obj,
+                                     int (*cb)(struct switchdev_obj *obj))
 {
        struct mv88e6xxx_atu_entry addr = {
                .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
@@ -2222,72 +2219,98 @@ static int _mv88e6xxx_port_fdb_dump_one(struct mv88e6xxx_chip *chip,
        do {
                err = _mv88e6xxx_atu_getnext(chip, fid, &addr);
                if (err)
-                       break;
+                       return err;
 
                if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
                        break;
 
-               if (!addr.trunk && addr.portv_trunkid & BIT(port)) {
-                       bool is_static = addr.state ==
-                               (is_multicast_ether_addr(addr.mac) ?
-                                GLOBAL_ATU_DATA_STATE_MC_STATIC :
-                                GLOBAL_ATU_DATA_STATE_UC_STATIC);
+               if (addr.trunk || (addr.portv_trunkid & BIT(port)) == 0)
+                       continue;
+
+               if (obj->id == SWITCHDEV_OBJ_ID_PORT_FDB) {
+                       struct switchdev_obj_port_fdb *fdb;
+
+                       if (!is_unicast_ether_addr(addr.mac))
+                               continue;
 
+                       fdb = SWITCHDEV_OBJ_PORT_FDB(obj);
                        fdb->vid = vid;
                        ether_addr_copy(fdb->addr, addr.mac);
-                       fdb->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
+                       if (addr.state == GLOBAL_ATU_DATA_STATE_UC_STATIC)
+                               fdb->ndm_state = NUD_NOARP;
+                       else
+                               fdb->ndm_state = NUD_REACHABLE;
+               } else if (obj->id == SWITCHDEV_OBJ_ID_PORT_MDB) {
+                       struct switchdev_obj_port_mdb *mdb;
 
-                       err = cb(&fdb->obj);
-                       if (err)
-                               break;
+                       if (!is_multicast_ether_addr(addr.mac))
+                               continue;
+
+                       mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
+                       mdb->vid = vid;
+                       ether_addr_copy(mdb->addr, addr.mac);
+               } else {
+                       return -EOPNOTSUPP;
                }
+
+               err = cb(obj);
+               if (err)
+                       return err;
        } while (!is_broadcast_ether_addr(addr.mac));
 
        return err;
 }
 
-static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
-                                  struct switchdev_obj_port_fdb *fdb,
-                                  int (*cb)(struct switchdev_obj *obj))
+static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
+                                 struct switchdev_obj *obj,
+                                 int (*cb)(struct switchdev_obj *obj))
 {
-       struct mv88e6xxx_chip *chip = ds_to_priv(ds);
        struct mv88e6xxx_vtu_stu_entry vlan = {
                .vid = GLOBAL_VTU_VID_MASK, /* all ones */
        };
        u16 fid;
        int err;
 
-       mutex_lock(&chip->reg_lock);
-
        /* Dump port's default Filtering Information Database (VLAN ID 0) */
        err = _mv88e6xxx_port_fid_get(chip, port, &fid);
        if (err)
-               goto unlock;
+               return err;
 
-       err = _mv88e6xxx_port_fdb_dump_one(chip, fid, 0, port, fdb, cb);
+       err = mv88e6xxx_port_db_dump_fid(chip, fid, 0, port, obj, cb);
        if (err)
-               goto unlock;
+               return err;
 
        /* Dump VLANs' Filtering Information Databases */
        err = _mv88e6xxx_vtu_vid_write(chip, vlan.vid);
        if (err)
-               goto unlock;
+               return err;
 
        do {
                err = _mv88e6xxx_vtu_getnext(chip, &vlan);
                if (err)
-                       break;
+                       return err;
 
                if (!vlan.valid)
                        break;
 
-               err = _mv88e6xxx_port_fdb_dump_one(chip, vlan.fid, vlan.vid,
-                                                  port, fdb, cb);
+               err = mv88e6xxx_port_db_dump_fid(chip, vlan.fid, vlan.vid, port,
+                                                obj, cb);
                if (err)
-                       break;
+                       return err;
        } while (vlan.vid < GLOBAL_VTU_VID_MASK);
 
-unlock:
+       return err;
+}
+
+static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
+                                  struct switchdev_obj_port_fdb *fdb,
+                                  int (*cb)(struct switchdev_obj *obj))
+{
+       struct mv88e6xxx_chip *chip = ds_to_priv(ds);
+       int err;
+
+       mutex_lock(&chip->reg_lock);
+       err = mv88e6xxx_port_db_dump(chip, port, &fdb->obj, cb);
        mutex_unlock(&chip->reg_lock);
 
        return err;
@@ -3980,6 +4003,58 @@ free:
        return NULL;
 }
 
+static int mv88e6xxx_port_mdb_prepare(struct dsa_switch *ds, int port,
+                                     const struct switchdev_obj_port_mdb *mdb,
+                                     struct switchdev_trans *trans)
+{
+       /* We don't need any dynamic resource from the kernel (yet),
+        * so skip the prepare phase.
+        */
+
+       return 0;
+}
+
+static void mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port,
+                                  const struct switchdev_obj_port_mdb *mdb,
+                                  struct switchdev_trans *trans)
+{
+       struct mv88e6xxx_chip *chip = ds_to_priv(ds);
+
+       mutex_lock(&chip->reg_lock);
+       if (mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
+                                        GLOBAL_ATU_DATA_STATE_MC_STATIC))
+               netdev_err(ds->ports[port].netdev, "failed to load multicast MAC address\n");
+       mutex_unlock(&chip->reg_lock);
+}
+
+static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port,
+                                 const struct switchdev_obj_port_mdb *mdb)
+{
+       struct mv88e6xxx_chip *chip = ds_to_priv(ds);
+       int err;
+
+       mutex_lock(&chip->reg_lock);
+       err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
+                                          GLOBAL_ATU_DATA_STATE_UNUSED);
+       mutex_unlock(&chip->reg_lock);
+
+       return err;
+}
+
+static int mv88e6xxx_port_mdb_dump(struct dsa_switch *ds, int port,
+                                  struct switchdev_obj_port_mdb *mdb,
+                                  int (*cb)(struct switchdev_obj *obj))
+{
+       struct mv88e6xxx_chip *chip = ds_to_priv(ds);
+       int err;
+
+       mutex_lock(&chip->reg_lock);
+       err = mv88e6xxx_port_db_dump(chip, port, &mdb->obj, cb);
+       mutex_unlock(&chip->reg_lock);
+
+       return err;
+}
+
 static struct dsa_switch_ops mv88e6xxx_switch_ops = {
        .probe                  = mv88e6xxx_drv_probe,
        .get_tag_protocol       = mv88e6xxx_get_tag_protocol,
@@ -4015,6 +4090,10 @@ static struct dsa_switch_ops mv88e6xxx_switch_ops = {
        .port_fdb_add           = mv88e6xxx_port_fdb_add,
        .port_fdb_del           = mv88e6xxx_port_fdb_del,
        .port_fdb_dump          = mv88e6xxx_port_fdb_dump,
+       .port_mdb_prepare       = mv88e6xxx_port_mdb_prepare,
+       .port_mdb_add           = mv88e6xxx_port_mdb_add,
+       .port_mdb_del           = mv88e6xxx_port_mdb_del,
+       .port_mdb_dump          = mv88e6xxx_port_mdb_dump,
 };
 
 static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip,
index 2ebeba4..e3eb230 100644 (file)
@@ -234,6 +234,7 @@ static inline u8 dsa_upstream_port(struct dsa_switch *ds)
 struct switchdev_trans;
 struct switchdev_obj;
 struct switchdev_obj_port_fdb;
+struct switchdev_obj_port_mdb;
 struct switchdev_obj_port_vlan;
 
 struct dsa_switch_ops {
@@ -369,6 +370,21 @@ struct dsa_switch_ops {
        int     (*port_fdb_dump)(struct dsa_switch *ds, int port,
                                 struct switchdev_obj_port_fdb *fdb,
                                 int (*cb)(struct switchdev_obj *obj));
+
+       /*
+        * Multicast database
+        */
+       int     (*port_mdb_prepare)(struct dsa_switch *ds, int port,
+                                   const struct switchdev_obj_port_mdb *mdb,
+                                   struct switchdev_trans *trans);
+       void    (*port_mdb_add)(struct dsa_switch *ds, int port,
+                               const struct switchdev_obj_port_mdb *mdb,
+                               struct switchdev_trans *trans);
+       int     (*port_mdb_del)(struct dsa_switch *ds, int port,
+                               const struct switchdev_obj_port_mdb *mdb);
+       int     (*port_mdb_dump)(struct dsa_switch *ds, int port,
+                                struct switchdev_obj_port_mdb *mdb,
+                                int (*cb)(struct switchdev_obj *obj));
 };
 
 void register_switch_driver(struct dsa_switch_ops *type);
index 9f6c2a2..9ecbe78 100644 (file)
@@ -290,6 +290,50 @@ static int dsa_slave_port_fdb_dump(struct net_device *dev,
        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;
+}
+
 static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
        struct dsa_slave_priv *p = netdev_priv(dev);
@@ -412,6 +456,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 +483,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 +510,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),