diff options
-rw-r--r-- | Documentation/networking/dsa/dsa.txt | 23 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.c | 163 | ||||
-rw-r--r-- | include/net/dsa.h | 16 | ||||
-rw-r--r-- | net/dsa/slave.c | 55 |
4 files changed, 215 insertions, 42 deletions
diff --git a/Documentation/networking/dsa/dsa.txt b/Documentation/networking/dsa/dsa.txt index a4e55c7..6d6c07c 100644 --- a/Documentation/networking/dsa/dsa.txt +++ b/Documentation/networking/dsa/dsa.txt @@ -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 ==== diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 4e697ee..e2a953e 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -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, diff --git a/include/net/dsa.h b/include/net/dsa.h index 2ebeba4..e3eb230 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -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); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 9f6c2a2..9ecbe78 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -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), |