Commit 07469f8c authored by David S. Miller's avatar David S. Miller

Merge branch 'dsa-mdb-support'

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: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents dbeb714a 7df8fbdd
......@@ -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
====
......
......@@ -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 @@ static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
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,
......
......@@ -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);
......
......@@ -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),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment