Commit 38db25ae authored by David S. Miller's avatar David S. Miller

Merge branch 'Update-DSAs-FDB-API-and-perform-switchdev-cleanup'

Arkadi Sharshevsky says:

====================
Update DSA's FDB API and perform switchdev cleanup

The patchset adds support for configuring static FDB entries via the
switchdev notification chain. The current method for FDB configuration
uses the switchdev's bridge bypass implementation. In order to support
this legacy way and to perform the switchdev cleanup, the implementation
is moved inside DSA.

The DSA drivers cannot sync the software bridge with hardware learned
entries and use the switchdev's implementation of bypass FDB dumping.
Because they are the only ones using this functionality, the fdb_dump
implementation is moved from switchdev code into DSA.

Finally after this changes a major cleanup in switchdev can be done.

Please see individual patches for patch specific change logs.
v1->v2
- Split MDB/vlan dump removal into core/driver removal.

v2->v3
- The self implementation for FDB add/del is moved inside DSA.
====================
Tested-by: default avatarVivien Didelot <vivien.didelot@savoirfairelinux.com>
Tested-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 511aeaf4 29ab586c
...@@ -1053,49 +1053,6 @@ int b53_vlan_del(struct dsa_switch *ds, int port, ...@@ -1053,49 +1053,6 @@ int b53_vlan_del(struct dsa_switch *ds, int port,
} }
EXPORT_SYMBOL(b53_vlan_del); EXPORT_SYMBOL(b53_vlan_del);
int b53_vlan_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_vlan *vlan,
switchdev_obj_dump_cb_t *cb)
{
struct b53_device *dev = ds->priv;
u16 vid, vid_start = 0, pvid;
struct b53_vlan *vl;
int err = 0;
if (is5325(dev) || is5365(dev))
vid_start = 1;
b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), &pvid);
/* Use our software cache for dumps, since we do not have any HW
* operation returning only the used/valid VLANs
*/
for (vid = vid_start; vid < dev->num_vlans; vid++) {
vl = &dev->vlans[vid];
if (!vl->valid)
continue;
if (!(vl->members & BIT(port)))
continue;
vlan->vid_begin = vlan->vid_end = vid;
vlan->flags = 0;
if (vl->untag & BIT(port))
vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
if (pvid == vid)
vlan->flags |= BRIDGE_VLAN_INFO_PVID;
err = cb(&vlan->obj);
if (err)
break;
}
return err;
}
EXPORT_SYMBOL(b53_vlan_dump);
/* Address Resolution Logic routines */ /* Address Resolution Logic routines */
static int b53_arl_op_wait(struct b53_device *dev) static int b53_arl_op_wait(struct b53_device *dev)
{ {
...@@ -1213,9 +1170,8 @@ static int b53_arl_op(struct b53_device *dev, int op, int port, ...@@ -1213,9 +1170,8 @@ static int b53_arl_op(struct b53_device *dev, int op, int port,
return b53_arl_rw_op(dev, 0); return b53_arl_rw_op(dev, 0);
} }
int b53_fdb_prepare(struct dsa_switch *ds, int port, int b53_fdb_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb, const unsigned char *addr, u16 vid)
struct switchdev_trans *trans)
{ {
struct b53_device *priv = ds->priv; struct b53_device *priv = ds->priv;
...@@ -1225,27 +1181,16 @@ int b53_fdb_prepare(struct dsa_switch *ds, int port, ...@@ -1225,27 +1181,16 @@ int b53_fdb_prepare(struct dsa_switch *ds, int port,
if (is5325(priv) || is5365(priv)) if (is5325(priv) || is5365(priv))
return -EOPNOTSUPP; return -EOPNOTSUPP;
return 0; return b53_arl_op(priv, 0, port, addr, vid, true);
}
EXPORT_SYMBOL(b53_fdb_prepare);
void b53_fdb_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb,
struct switchdev_trans *trans)
{
struct b53_device *priv = ds->priv;
if (b53_arl_op(priv, 0, port, fdb->addr, fdb->vid, true))
pr_err("%s: failed to add MAC address\n", __func__);
} }
EXPORT_SYMBOL(b53_fdb_add); EXPORT_SYMBOL(b53_fdb_add);
int b53_fdb_del(struct dsa_switch *ds, int port, int b53_fdb_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb) const unsigned char *addr, u16 vid)
{ {
struct b53_device *priv = ds->priv; struct b53_device *priv = ds->priv;
return b53_arl_op(priv, 0, port, fdb->addr, fdb->vid, false); return b53_arl_op(priv, 0, port, addr, vid, false);
} }
EXPORT_SYMBOL(b53_fdb_del); EXPORT_SYMBOL(b53_fdb_del);
...@@ -1282,8 +1227,7 @@ static void b53_arl_search_rd(struct b53_device *dev, u8 idx, ...@@ -1282,8 +1227,7 @@ static void b53_arl_search_rd(struct b53_device *dev, u8 idx,
} }
static int b53_fdb_copy(int port, const struct b53_arl_entry *ent, static int b53_fdb_copy(int port, const struct b53_arl_entry *ent,
struct switchdev_obj_port_fdb *fdb, dsa_fdb_dump_cb_t *cb, void *data)
switchdev_obj_dump_cb_t *cb)
{ {
if (!ent->is_valid) if (!ent->is_valid)
return 0; return 0;
...@@ -1291,16 +1235,11 @@ static int b53_fdb_copy(int port, const struct b53_arl_entry *ent, ...@@ -1291,16 +1235,11 @@ static int b53_fdb_copy(int port, const struct b53_arl_entry *ent,
if (port != ent->port) if (port != ent->port)
return 0; return 0;
ether_addr_copy(fdb->addr, ent->mac); return cb(ent->mac, ent->vid, ent->is_static, data);
fdb->vid = ent->vid;
fdb->ndm_state = ent->is_static ? NUD_NOARP : NUD_REACHABLE;
return cb(&fdb->obj);
} }
int b53_fdb_dump(struct dsa_switch *ds, int port, int b53_fdb_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_fdb *fdb, dsa_fdb_dump_cb_t *cb, void *data)
switchdev_obj_dump_cb_t *cb)
{ {
struct b53_device *priv = ds->priv; struct b53_device *priv = ds->priv;
struct b53_arl_entry results[2]; struct b53_arl_entry results[2];
...@@ -1318,13 +1257,13 @@ int b53_fdb_dump(struct dsa_switch *ds, int port, ...@@ -1318,13 +1257,13 @@ int b53_fdb_dump(struct dsa_switch *ds, int port,
return ret; return ret;
b53_arl_search_rd(priv, 0, &results[0]); b53_arl_search_rd(priv, 0, &results[0]);
ret = b53_fdb_copy(port, &results[0], fdb, cb); ret = b53_fdb_copy(port, &results[0], cb, data);
if (ret) if (ret)
return ret; return ret;
if (priv->num_arl_entries > 2) { if (priv->num_arl_entries > 2) {
b53_arl_search_rd(priv, 1, &results[1]); b53_arl_search_rd(priv, 1, &results[1]);
ret = b53_fdb_copy(port, &results[1], fdb, cb); ret = b53_fdb_copy(port, &results[1], cb, data);
if (ret) if (ret)
return ret; return ret;
...@@ -1564,8 +1503,6 @@ static const struct dsa_switch_ops b53_switch_ops = { ...@@ -1564,8 +1503,6 @@ static const struct dsa_switch_ops b53_switch_ops = {
.port_vlan_prepare = b53_vlan_prepare, .port_vlan_prepare = b53_vlan_prepare,
.port_vlan_add = b53_vlan_add, .port_vlan_add = b53_vlan_add,
.port_vlan_del = b53_vlan_del, .port_vlan_del = b53_vlan_del,
.port_vlan_dump = b53_vlan_dump,
.port_fdb_prepare = b53_fdb_prepare,
.port_fdb_dump = b53_fdb_dump, .port_fdb_dump = b53_fdb_dump,
.port_fdb_add = b53_fdb_add, .port_fdb_add = b53_fdb_add,
.port_fdb_del = b53_fdb_del, .port_fdb_del = b53_fdb_del,
......
...@@ -393,20 +393,12 @@ void b53_vlan_add(struct dsa_switch *ds, int port, ...@@ -393,20 +393,12 @@ void b53_vlan_add(struct dsa_switch *ds, int port,
struct switchdev_trans *trans); struct switchdev_trans *trans);
int b53_vlan_del(struct dsa_switch *ds, int port, int b53_vlan_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan); const struct switchdev_obj_port_vlan *vlan);
int b53_vlan_dump(struct dsa_switch *ds, int port, int b53_fdb_add(struct dsa_switch *ds, int port,
struct switchdev_obj_port_vlan *vlan, const unsigned char *addr, u16 vid);
switchdev_obj_dump_cb_t *cb);
int b53_fdb_prepare(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb,
struct switchdev_trans *trans);
void b53_fdb_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb,
struct switchdev_trans *trans);
int b53_fdb_del(struct dsa_switch *ds, int port, int b53_fdb_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb); const unsigned char *addr, u16 vid);
int b53_fdb_dump(struct dsa_switch *ds, int port, int b53_fdb_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_fdb *fdb, dsa_fdb_dump_cb_t *cb, void *data);
switchdev_obj_dump_cb_t *cb);
int b53_mirror_add(struct dsa_switch *ds, int port, int b53_mirror_add(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror, bool ingress); struct dsa_mall_mirror_tc_entry *mirror, bool ingress);
void b53_mirror_del(struct dsa_switch *ds, int port, void b53_mirror_del(struct dsa_switch *ds, int port,
......
...@@ -1021,8 +1021,6 @@ static const struct dsa_switch_ops bcm_sf2_ops = { ...@@ -1021,8 +1021,6 @@ static const struct dsa_switch_ops bcm_sf2_ops = {
.port_vlan_prepare = b53_vlan_prepare, .port_vlan_prepare = b53_vlan_prepare,
.port_vlan_add = b53_vlan_add, .port_vlan_add = b53_vlan_add,
.port_vlan_del = b53_vlan_del, .port_vlan_del = b53_vlan_del,
.port_vlan_dump = b53_vlan_dump,
.port_fdb_prepare = b53_fdb_prepare,
.port_fdb_dump = b53_fdb_dump, .port_fdb_dump = b53_fdb_dump,
.port_fdb_add = b53_fdb_add, .port_fdb_add = b53_fdb_add,
.port_fdb_del = b53_fdb_del, .port_fdb_del = b53_fdb_del,
......
...@@ -257,43 +257,6 @@ static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port, ...@@ -257,43 +257,6 @@ static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port,
return 0; return 0;
} }
static int dsa_loop_port_vlan_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_vlan *vlan,
switchdev_obj_dump_cb_t *cb)
{
struct dsa_loop_priv *ps = ds->priv;
struct mii_bus *bus = ps->bus;
struct dsa_loop_vlan *vl;
u16 vid, vid_start = 0;
int err = 0;
dev_dbg(ds->dev, "%s\n", __func__);
/* Just do a sleeping operation to make lockdep checks effective */
mdiobus_read(bus, ps->port_base + port, MII_BMSR);
for (vid = vid_start; vid < DSA_LOOP_VLANS; vid++) {
vl = &ps->vlans[vid];
if (!(vl->members & BIT(port)))
continue;
vlan->vid_begin = vlan->vid_end = vid;
vlan->flags = 0;
if (vl->untagged & BIT(port))
vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
if (ps->pvid == vid)
vlan->flags |= BRIDGE_VLAN_INFO_PVID;
err = cb(&vlan->obj);
if (err)
break;
}
return err;
}
static struct dsa_switch_ops dsa_loop_driver = { static struct dsa_switch_ops dsa_loop_driver = {
.get_tag_protocol = dsa_loop_get_protocol, .get_tag_protocol = dsa_loop_get_protocol,
.setup = dsa_loop_setup, .setup = dsa_loop_setup,
...@@ -310,7 +273,6 @@ static struct dsa_switch_ops dsa_loop_driver = { ...@@ -310,7 +273,6 @@ static struct dsa_switch_ops dsa_loop_driver = {
.port_vlan_prepare = dsa_loop_port_vlan_prepare, .port_vlan_prepare = dsa_loop_port_vlan_prepare,
.port_vlan_add = dsa_loop_port_vlan_add, .port_vlan_add = dsa_loop_port_vlan_add,
.port_vlan_del = dsa_loop_port_vlan_del, .port_vlan_del = dsa_loop_port_vlan_del,
.port_vlan_dump = dsa_loop_port_vlan_dump,
}; };
static int dsa_loop_drv_probe(struct mdio_device *mdiodev) static int dsa_loop_drv_probe(struct mdio_device *mdiodev)
......
...@@ -638,55 +638,6 @@ static int ksz_port_vlan_del(struct dsa_switch *ds, int port, ...@@ -638,55 +638,6 @@ static int ksz_port_vlan_del(struct dsa_switch *ds, int port,
return 0; return 0;
} }
static int ksz_port_vlan_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_vlan *vlan,
switchdev_obj_dump_cb_t *cb)
{
struct ksz_device *dev = ds->priv;
u16 vid;
u16 data;
struct vlan_table *vlan_cache;
int err = 0;
mutex_lock(&dev->vlan_mutex);
/* use dev->vlan_cache due to lack of searching valid vlan entry */
for (vid = vlan->vid_begin; vid < dev->num_vlans; vid++) {
vlan_cache = &dev->vlan_cache[vid];
if (!(vlan_cache->table[0] & VLAN_VALID))
continue;
vlan->vid_begin = vid;
vlan->vid_end = vid;
vlan->flags = 0;
if (vlan_cache->table[2] & BIT(port)) {
if (vlan_cache->table[1] & BIT(port))
vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
ksz_pread16(dev, port, REG_PORT_DEFAULT_VID, &data);
if (vid == (data & 0xFFFFF))
vlan->flags |= BRIDGE_VLAN_INFO_PVID;
err = cb(&vlan->obj);
if (err)
break;
}
}
mutex_unlock(&dev->vlan_mutex);
return err;
}
static int ksz_port_fdb_prepare(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb,
struct switchdev_trans *trans)
{
/* nothing needed */
return 0;
}
struct alu_struct { struct alu_struct {
/* entry 1 */ /* entry 1 */
u8 is_static:1; u8 is_static:1;
...@@ -706,30 +657,31 @@ struct alu_struct { ...@@ -706,30 +657,31 @@ struct alu_struct {
u8 mac[ETH_ALEN]; u8 mac[ETH_ALEN];
}; };
static void ksz_port_fdb_add(struct dsa_switch *ds, int port, static int ksz_port_fdb_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb, const unsigned char *addr, u16 vid)
struct switchdev_trans *trans)
{ {
struct ksz_device *dev = ds->priv; struct ksz_device *dev = ds->priv;
u32 alu_table[4]; u32 alu_table[4];
u32 data; u32 data;
int ret = 0;
mutex_lock(&dev->alu_mutex); mutex_lock(&dev->alu_mutex);
/* find any entry with mac & vid */ /* find any entry with mac & vid */
data = fdb->vid << ALU_FID_INDEX_S; data = vid << ALU_FID_INDEX_S;
data |= ((fdb->addr[0] << 8) | fdb->addr[1]); data |= ((addr[0] << 8) | addr[1]);
ksz_write32(dev, REG_SW_ALU_INDEX_0, data); ksz_write32(dev, REG_SW_ALU_INDEX_0, data);
data = ((fdb->addr[2] << 24) | (fdb->addr[3] << 16)); data = ((addr[2] << 24) | (addr[3] << 16));
data |= ((fdb->addr[4] << 8) | fdb->addr[5]); data |= ((addr[4] << 8) | addr[5]);
ksz_write32(dev, REG_SW_ALU_INDEX_1, data); ksz_write32(dev, REG_SW_ALU_INDEX_1, data);
/* start read operation */ /* start read operation */
ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_READ | ALU_START); ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_READ | ALU_START);
/* wait to be finished */ /* wait to be finished */
if (wait_alu_ready(dev, ALU_START, 1000) < 0) { ret = wait_alu_ready(dev, ALU_START, 1000);
if (ret < 0) {
dev_dbg(dev->dev, "Failed to read ALU\n"); dev_dbg(dev->dev, "Failed to read ALU\n");
goto exit; goto exit;
} }
...@@ -740,27 +692,30 @@ static void ksz_port_fdb_add(struct dsa_switch *ds, int port, ...@@ -740,27 +692,30 @@ static void ksz_port_fdb_add(struct dsa_switch *ds, int port,
/* update ALU entry */ /* update ALU entry */
alu_table[0] = ALU_V_STATIC_VALID; alu_table[0] = ALU_V_STATIC_VALID;
alu_table[1] |= BIT(port); alu_table[1] |= BIT(port);
if (fdb->vid) if (vid)
alu_table[1] |= ALU_V_USE_FID; alu_table[1] |= ALU_V_USE_FID;
alu_table[2] = (fdb->vid << ALU_V_FID_S); alu_table[2] = (vid << ALU_V_FID_S);
alu_table[2] |= ((fdb->addr[0] << 8) | fdb->addr[1]); alu_table[2] |= ((addr[0] << 8) | addr[1]);
alu_table[3] = ((fdb->addr[2] << 24) | (fdb->addr[3] << 16)); alu_table[3] = ((addr[2] << 24) | (addr[3] << 16));
alu_table[3] |= ((fdb->addr[4] << 8) | fdb->addr[5]); alu_table[3] |= ((addr[4] << 8) | addr[5]);
write_table(ds, alu_table); write_table(ds, alu_table);
ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_WRITE | ALU_START); ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_WRITE | ALU_START);
/* wait to be finished */ /* wait to be finished */
if (wait_alu_ready(dev, ALU_START, 1000) < 0) ret = wait_alu_ready(dev, ALU_START, 1000);
dev_dbg(dev->dev, "Failed to read ALU\n"); if (ret < 0)
dev_dbg(dev->dev, "Failed to write ALU\n");
exit: exit:
mutex_unlock(&dev->alu_mutex); mutex_unlock(&dev->alu_mutex);
return ret;
} }
static int ksz_port_fdb_del(struct dsa_switch *ds, int port, static int ksz_port_fdb_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb) const unsigned char *addr, u16 vid)
{ {
struct ksz_device *dev = ds->priv; struct ksz_device *dev = ds->priv;
u32 alu_table[4]; u32 alu_table[4];
...@@ -770,12 +725,12 @@ static int ksz_port_fdb_del(struct dsa_switch *ds, int port, ...@@ -770,12 +725,12 @@ static int ksz_port_fdb_del(struct dsa_switch *ds, int port,
mutex_lock(&dev->alu_mutex); mutex_lock(&dev->alu_mutex);
/* read any entry with mac & vid */ /* read any entry with mac & vid */
data = fdb->vid << ALU_FID_INDEX_S; data = vid << ALU_FID_INDEX_S;
data |= ((fdb->addr[0] << 8) | fdb->addr[1]); data |= ((addr[0] << 8) | addr[1]);
ksz_write32(dev, REG_SW_ALU_INDEX_0, data); ksz_write32(dev, REG_SW_ALU_INDEX_0, data);
data = ((fdb->addr[2] << 24) | (fdb->addr[3] << 16)); data = ((addr[2] << 24) | (addr[3] << 16));
data |= ((fdb->addr[4] << 8) | fdb->addr[5]); data |= ((addr[4] << 8) | addr[5]);
ksz_write32(dev, REG_SW_ALU_INDEX_1, data); ksz_write32(dev, REG_SW_ALU_INDEX_1, data);
/* start read operation */ /* start read operation */
...@@ -850,12 +805,11 @@ static void convert_alu(struct alu_struct *alu, u32 *alu_table) ...@@ -850,12 +805,11 @@ static void convert_alu(struct alu_struct *alu, u32 *alu_table)
} }
static int ksz_port_fdb_dump(struct dsa_switch *ds, int port, static int ksz_port_fdb_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_fdb *fdb, dsa_fdb_dump_cb_t *cb, void *data)
switchdev_obj_dump_cb_t *cb)
{ {
struct ksz_device *dev = ds->priv; struct ksz_device *dev = ds->priv;
int ret = 0; int ret = 0;
u32 data; u32 ksz_data;
u32 alu_table[4]; u32 alu_table[4];
struct alu_struct alu; struct alu_struct alu;
int timeout; int timeout;
...@@ -868,8 +822,8 @@ static int ksz_port_fdb_dump(struct dsa_switch *ds, int port, ...@@ -868,8 +822,8 @@ static int ksz_port_fdb_dump(struct dsa_switch *ds, int port,
do { do {
timeout = 1000; timeout = 1000;
do { do {
ksz_read32(dev, REG_SW_ALU_CTRL__4, &data); ksz_read32(dev, REG_SW_ALU_CTRL__4, &ksz_data);
if ((data & ALU_VALID) || !(data & ALU_START)) if ((ksz_data & ALU_VALID) || !(ksz_data & ALU_START))
break; break;
usleep_range(1, 10); usleep_range(1, 10);
} while (timeout-- > 0); } while (timeout-- > 0);
...@@ -886,18 +840,11 @@ static int ksz_port_fdb_dump(struct dsa_switch *ds, int port, ...@@ -886,18 +840,11 @@ static int ksz_port_fdb_dump(struct dsa_switch *ds, int port,
convert_alu(&alu, alu_table); convert_alu(&alu, alu_table);
if (alu.port_forward & BIT(port)) { if (alu.port_forward & BIT(port)) {
fdb->vid = alu.fid; ret = cb(alu.mac, alu.fid, alu.is_static, data);
if (alu.is_static)
fdb->ndm_state = NUD_NOARP;
else
fdb->ndm_state = NUD_REACHABLE;
ether_addr_copy(fdb->addr, alu.mac);
ret = cb(&fdb->obj);
if (ret) if (ret)
goto exit; goto exit;
} }
} while (data & ALU_START); } while (ksz_data & ALU_START);
exit: exit:
...@@ -1065,14 +1012,6 @@ static int ksz_port_mdb_del(struct dsa_switch *ds, int port, ...@@ -1065,14 +1012,6 @@ static int ksz_port_mdb_del(struct dsa_switch *ds, int port,
return ret; return ret;
} }
static int ksz_port_mdb_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_mdb *mdb,
switchdev_obj_dump_cb_t *cb)
{
/* this is not called by switch layer */
return 0;
}
static int ksz_port_mirror_add(struct dsa_switch *ds, int port, static int ksz_port_mirror_add(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror, struct dsa_mall_mirror_tc_entry *mirror,
bool ingress) bool ingress)
...@@ -1129,15 +1068,12 @@ static const struct dsa_switch_ops ksz_switch_ops = { ...@@ -1129,15 +1068,12 @@ static const struct dsa_switch_ops ksz_switch_ops = {
.port_vlan_prepare = ksz_port_vlan_prepare, .port_vlan_prepare = ksz_port_vlan_prepare,
.port_vlan_add = ksz_port_vlan_add, .port_vlan_add = ksz_port_vlan_add,
.port_vlan_del = ksz_port_vlan_del, .port_vlan_del = ksz_port_vlan_del,
.port_vlan_dump = ksz_port_vlan_dump,
.port_fdb_prepare = ksz_port_fdb_prepare,
.port_fdb_dump = ksz_port_fdb_dump, .port_fdb_dump = ksz_port_fdb_dump,
.port_fdb_add = ksz_port_fdb_add, .port_fdb_add = ksz_port_fdb_add,
.port_fdb_del = ksz_port_fdb_del, .port_fdb_del = ksz_port_fdb_del,
.port_mdb_prepare = ksz_port_mdb_prepare, .port_mdb_prepare = ksz_port_mdb_prepare,
.port_mdb_add = ksz_port_mdb_add, .port_mdb_add = ksz_port_mdb_add,
.port_mdb_del = ksz_port_mdb_del, .port_mdb_del = ksz_port_mdb_del,
.port_mdb_dump = ksz_port_mdb_dump,
.port_mirror_add = ksz_port_mirror_add, .port_mirror_add = ksz_port_mirror_add,
.port_mirror_del = ksz_port_mirror_del, .port_mirror_del = ksz_port_mirror_del,
}; };
......
...@@ -801,49 +801,31 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port, ...@@ -801,49 +801,31 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port,
} }
static int static int
mt7530_port_fdb_prepare(struct dsa_switch *ds, int port, mt7530_port_fdb_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb, const unsigned char *addr, u16 vid)
struct switchdev_trans *trans)
{ {
struct mt7530_priv *priv = ds->priv; struct mt7530_priv *priv = ds->priv;
int ret; int ret;
u8 port_mask = BIT(port);
/* Because auto-learned entrie shares the same FDB table.
* an entry is reserved with no port_mask to make sure fdb_add
* is called while the entry is still available.
*/
mutex_lock(&priv->reg_mutex); mutex_lock(&priv->reg_mutex);
mt7530_fdb_write(priv, fdb->vid, 0, fdb->addr, -1, STATIC_ENT); mt7530_fdb_write(priv, vid, port_mask, addr, -1, STATIC_ENT);
ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0);
mutex_unlock(&priv->reg_mutex); mutex_unlock(&priv->reg_mutex);
return ret; return ret;
} }
static void
mt7530_port_fdb_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb,
struct switchdev_trans *trans)
{
struct mt7530_priv *priv = ds->priv;
u8 port_mask = BIT(port);
mutex_lock(&priv->reg_mutex);
mt7530_fdb_write(priv, fdb->vid, port_mask, fdb->addr, -1, STATIC_ENT);
mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0);
mutex_unlock(&priv->reg_mutex);
}
static int static int
mt7530_port_fdb_del(struct dsa_switch *ds, int port, mt7530_port_fdb_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb) const unsigned char *addr, u16 vid)
{ {
struct mt7530_priv *priv = ds->priv; struct mt7530_priv *priv = ds->priv;
int ret; int ret;
u8 port_mask = BIT(port); u8 port_mask = BIT(port);
mutex_lock(&priv->reg_mutex); mutex_lock(&priv->reg_mutex);
mt7530_fdb_write(priv, fdb->vid, port_mask, fdb->addr, -1, STATIC_EMP); mt7530_fdb_write(priv, vid, port_mask, addr, -1, STATIC_EMP);
ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0);
mutex_unlock(&priv->reg_mutex); mutex_unlock(&priv->reg_mutex);
...@@ -852,8 +834,7 @@ mt7530_port_fdb_del(struct dsa_switch *ds, int port, ...@@ -852,8 +834,7 @@ mt7530_port_fdb_del(struct dsa_switch *ds, int port,
static int static int
mt7530_port_fdb_dump(struct dsa_switch *ds, int port, mt7530_port_fdb_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_fdb *fdb, dsa_fdb_dump_cb_t *cb, void *data)
switchdev_obj_dump_cb_t *cb)
{ {
struct mt7530_priv *priv = ds->priv; struct mt7530_priv *priv = ds->priv;
struct mt7530_fdb _fdb = { 0 }; struct mt7530_fdb _fdb = { 0 };
...@@ -871,11 +852,8 @@ mt7530_port_fdb_dump(struct dsa_switch *ds, int port, ...@@ -871,11 +852,8 @@ mt7530_port_fdb_dump(struct dsa_switch *ds, int port,
if (rsp & ATC_SRCH_HIT) { if (rsp & ATC_SRCH_HIT) {
mt7530_fdb_read(priv, &_fdb); mt7530_fdb_read(priv, &_fdb);
if (_fdb.port_mask & BIT(port)) { if (_fdb.port_mask & BIT(port)) {
ether_addr_copy(fdb->addr, _fdb.mac); ret = cb(_fdb.mac, _fdb.vid, _fdb.noarp,
fdb->vid = _fdb.vid; data);
fdb->ndm_state = _fdb.noarp ?
NUD_NOARP : NUD_REACHABLE;
ret = cb(&fdb->obj);
if (ret < 0) if (ret < 0)
break; break;
} }
...@@ -1015,7 +993,6 @@ static struct dsa_switch_ops mt7530_switch_ops = { ...@@ -1015,7 +993,6 @@ static struct dsa_switch_ops mt7530_switch_ops = {
.port_stp_state_set = mt7530_stp_state_set, .port_stp_state_set = mt7530_stp_state_set,
.port_bridge_join = mt7530_port_bridge_join, .port_bridge_join = mt7530_port_bridge_join,
.port_bridge_leave = mt7530_port_bridge_leave, .port_bridge_leave = mt7530_port_bridge_leave,
.port_fdb_prepare = mt7530_port_fdb_prepare,
.port_fdb_add = mt7530_port_fdb_add, .port_fdb_add = mt7530_port_fdb_add,
.port_fdb_del = mt7530_port_fdb_del, .port_fdb_del = mt7530_port_fdb_del,
.port_fdb_dump = mt7530_port_fdb_dump, .port_fdb_dump = mt7530_port_fdb_dump,
......
...@@ -1011,61 +1011,6 @@ static int mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip, ...@@ -1011,61 +1011,6 @@ static int mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip,
return chip->info->ops->vtu_loadpurge(chip, entry); return chip->info->ops->vtu_loadpurge(chip, entry);
} }
static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_vlan *vlan,
switchdev_obj_dump_cb_t *cb)
{
struct mv88e6xxx_chip *chip = ds->priv;
struct mv88e6xxx_vtu_entry next = {
.vid = chip->info->max_vid,
};
u16 pvid;
int err;
if (!chip->info->max_vid)
return -EOPNOTSUPP;
mutex_lock(&chip->reg_lock);
err = mv88e6xxx_port_get_pvid(chip, port, &pvid);
if (err)
goto unlock;
do {
err = mv88e6xxx_vtu_getnext(chip, &next);
if (err)
break;
if (!next.valid)
break;
if (next.member[port] ==
MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER)
continue;
/* reinit and dump this VLAN obj */
vlan->vid_begin = next.vid;
vlan->vid_end = next.vid;
vlan->flags = 0;
if (next.member[port] ==
MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNTAGGED)
vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
if (next.vid == pvid)
vlan->flags |= BRIDGE_VLAN_INFO_PVID;
err = cb(&vlan->obj);
if (err)
break;
} while (next.vid < chip->info->max_vid);
unlock:
mutex_unlock(&chip->reg_lock);
return err;
}
static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid)
{ {
DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
...@@ -1406,38 +1351,28 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, ...@@ -1406,38 +1351,28 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
return mv88e6xxx_g1_atu_loadpurge(chip, vlan.fid, &entry); return mv88e6xxx_g1_atu_loadpurge(chip, vlan.fid, &entry);
} }
static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, static int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb, const unsigned char *addr, u16 vid)
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_fdb_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb,
struct switchdev_trans *trans)
{ {
struct mv88e6xxx_chip *chip = ds->priv; struct mv88e6xxx_chip *chip = ds->priv;
int err;
mutex_lock(&chip->reg_lock); mutex_lock(&chip->reg_lock);
if (mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid, err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid,
MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC)) MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC);
dev_err(ds->dev, "p%d: failed to load unicast MAC address\n",
port);
mutex_unlock(&chip->reg_lock); mutex_unlock(&chip->reg_lock);
return err;
} }
static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb) const unsigned char *addr, u16 vid)
{ {
struct mv88e6xxx_chip *chip = ds->priv; struct mv88e6xxx_chip *chip = ds->priv;
int err; int err;
mutex_lock(&chip->reg_lock); mutex_lock(&chip->reg_lock);
err = mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid, err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid,
MV88E6XXX_G1_ATU_DATA_STATE_UNUSED); MV88E6XXX_G1_ATU_DATA_STATE_UNUSED);
mutex_unlock(&chip->reg_lock); mutex_unlock(&chip->reg_lock);
...@@ -1446,10 +1381,10 @@ static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, ...@@ -1446,10 +1381,10 @@ static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip, static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
u16 fid, u16 vid, int port, u16 fid, u16 vid, int port,
struct switchdev_obj *obj, dsa_fdb_dump_cb_t *cb, void *data)
switchdev_obj_dump_cb_t *cb)
{ {
struct mv88e6xxx_atu_entry addr; struct mv88e6xxx_atu_entry addr;
bool is_static;
int err; int err;
addr.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED; addr.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED;
...@@ -1466,33 +1401,12 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip, ...@@ -1466,33 +1401,12 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
if (addr.trunk || (addr.portvec & BIT(port)) == 0) if (addr.trunk || (addr.portvec & BIT(port)) == 0)
continue; continue;
if (obj->id == SWITCHDEV_OBJ_ID_PORT_FDB) {
struct switchdev_obj_port_fdb *fdb;
if (!is_unicast_ether_addr(addr.mac)) if (!is_unicast_ether_addr(addr.mac))
continue; continue;
fdb = SWITCHDEV_OBJ_PORT_FDB(obj); is_static = (addr.state ==
fdb->vid = vid; MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC);
ether_addr_copy(fdb->addr, addr.mac); err = cb(addr.mac, vid, is_static, data);
if (addr.state == MV88E6XXX_G1_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;
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) if (err)
return err; return err;
} while (!is_broadcast_ether_addr(addr.mac)); } while (!is_broadcast_ether_addr(addr.mac));
...@@ -1501,8 +1415,7 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip, ...@@ -1501,8 +1415,7 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
} }
static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
struct switchdev_obj *obj, dsa_fdb_dump_cb_t *cb, void *data)
switchdev_obj_dump_cb_t *cb)
{ {
struct mv88e6xxx_vtu_entry vlan = { struct mv88e6xxx_vtu_entry vlan = {
.vid = chip->info->max_vid, .vid = chip->info->max_vid,
...@@ -1515,7 +1428,7 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, ...@@ -1515,7 +1428,7 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
if (err) if (err)
return err; return err;
err = mv88e6xxx_port_db_dump_fid(chip, fid, 0, port, obj, cb); err = mv88e6xxx_port_db_dump_fid(chip, fid, 0, port, cb, data);
if (err) if (err)
return err; return err;
...@@ -1529,7 +1442,7 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, ...@@ -1529,7 +1442,7 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
break; break;
err = mv88e6xxx_port_db_dump_fid(chip, vlan.fid, vlan.vid, port, err = mv88e6xxx_port_db_dump_fid(chip, vlan.fid, vlan.vid, port,
obj, cb); cb, data);
if (err) if (err)
return err; return err;
} while (vlan.vid < chip->info->max_vid); } while (vlan.vid < chip->info->max_vid);
...@@ -1538,14 +1451,13 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, ...@@ -1538,14 +1451,13 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
} }
static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_fdb *fdb, dsa_fdb_dump_cb_t *cb, void *data)
switchdev_obj_dump_cb_t *cb)
{ {
struct mv88e6xxx_chip *chip = ds->priv; struct mv88e6xxx_chip *chip = ds->priv;
int err; int err;
mutex_lock(&chip->reg_lock); mutex_lock(&chip->reg_lock);
err = mv88e6xxx_port_db_dump(chip, port, &fdb->obj, cb); err = mv88e6xxx_port_db_dump(chip, port, cb, data);
mutex_unlock(&chip->reg_lock); mutex_unlock(&chip->reg_lock);
return err; return err;
...@@ -3865,20 +3777,6 @@ static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port, ...@@ -3865,20 +3777,6 @@ static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port,
return err; return err;
} }
static int mv88e6xxx_port_mdb_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_mdb *mdb,
switchdev_obj_dump_cb_t *cb)
{
struct mv88e6xxx_chip *chip = ds->priv;
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 const struct dsa_switch_ops mv88e6xxx_switch_ops = { static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.probe = mv88e6xxx_drv_probe, .probe = mv88e6xxx_drv_probe,
.get_tag_protocol = mv88e6xxx_get_tag_protocol, .get_tag_protocol = mv88e6xxx_get_tag_protocol,
...@@ -3906,15 +3804,12 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { ...@@ -3906,15 +3804,12 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.port_vlan_prepare = mv88e6xxx_port_vlan_prepare, .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
.port_vlan_add = mv88e6xxx_port_vlan_add, .port_vlan_add = mv88e6xxx_port_vlan_add,
.port_vlan_del = mv88e6xxx_port_vlan_del, .port_vlan_del = mv88e6xxx_port_vlan_del,
.port_vlan_dump = mv88e6xxx_port_vlan_dump,
.port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
.port_fdb_add = mv88e6xxx_port_fdb_add, .port_fdb_add = mv88e6xxx_port_fdb_add,
.port_fdb_del = mv88e6xxx_port_fdb_del, .port_fdb_del = mv88e6xxx_port_fdb_del,
.port_fdb_dump = mv88e6xxx_port_fdb_dump, .port_fdb_dump = mv88e6xxx_port_fdb_dump,
.port_mdb_prepare = mv88e6xxx_port_mdb_prepare, .port_mdb_prepare = mv88e6xxx_port_mdb_prepare,
.port_mdb_add = mv88e6xxx_port_mdb_add, .port_mdb_add = mv88e6xxx_port_mdb_add,
.port_mdb_del = mv88e6xxx_port_mdb_del, .port_mdb_del = mv88e6xxx_port_mdb_del,
.port_mdb_dump = mv88e6xxx_port_mdb_dump,
.crosschip_bridge_join = mv88e6xxx_crosschip_bridge_join, .crosschip_bridge_join = mv88e6xxx_crosschip_bridge_join,
.crosschip_bridge_leave = mv88e6xxx_crosschip_bridge_leave, .crosschip_bridge_leave = mv88e6xxx_crosschip_bridge_leave,
}; };
......
...@@ -777,69 +777,44 @@ qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, ...@@ -777,69 +777,44 @@ qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr,
} }
static int static int
qca8k_port_fdb_prepare(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb,
struct switchdev_trans *trans)
{
struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
/* The FDB table for static and auto learned entries is the same. We
* need to reserve an entry with no port_mask set to make sure that
* when port_fdb_add is called an entry is still available. Otherwise
* the last free entry might have been used up by auto learning
*/
return qca8k_port_fdb_insert(priv, fdb->addr, 0, fdb->vid);
}
static void
qca8k_port_fdb_add(struct dsa_switch *ds, int port, qca8k_port_fdb_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb, const unsigned char *addr, u16 vid)
struct switchdev_trans *trans)
{ {
struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
u16 port_mask = BIT(port); u16 port_mask = BIT(port);
/* Update the FDB entry adding the port_mask */ return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
qca8k_port_fdb_insert(priv, fdb->addr, port_mask, fdb->vid);
} }
static int static int
qca8k_port_fdb_del(struct dsa_switch *ds, int port, qca8k_port_fdb_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb) const unsigned char *addr, u16 vid)
{ {
struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
u16 port_mask = BIT(port); u16 port_mask = BIT(port);
u16 vid = fdb->vid;
if (!vid) if (!vid)
vid = 1; vid = 1;
return qca8k_fdb_del(priv, fdb->addr, port_mask, vid); return qca8k_fdb_del(priv, addr, port_mask, vid);
} }
static int static int
qca8k_port_fdb_dump(struct dsa_switch *ds, int port, qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_fdb *fdb, dsa_fdb_dump_cb_t *cb, void *data)
switchdev_obj_dump_cb_t *cb)
{ {
struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
struct qca8k_fdb _fdb = { 0 }; struct qca8k_fdb _fdb = { 0 };
int cnt = QCA8K_NUM_FDB_RECORDS; int cnt = QCA8K_NUM_FDB_RECORDS;
bool is_static;
int ret = 0; int ret = 0;
mutex_lock(&priv->reg_mutex); mutex_lock(&priv->reg_mutex);
while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) {
if (!_fdb.aging) if (!_fdb.aging)
break; break;
is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC);
ether_addr_copy(fdb->addr, _fdb.mac); ret = cb(_fdb.mac, _fdb.vid, is_static, data);
fdb->vid = _fdb.vid;
if (_fdb.aging == QCA8K_ATU_STATUS_STATIC)
fdb->ndm_state = NUD_NOARP;
else
fdb->ndm_state = NUD_REACHABLE;
ret = cb(&fdb->obj);
if (ret) if (ret)
break; break;
} }
...@@ -869,7 +844,6 @@ static const struct dsa_switch_ops qca8k_switch_ops = { ...@@ -869,7 +844,6 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
.port_stp_state_set = qca8k_port_stp_state_set, .port_stp_state_set = qca8k_port_stp_state_set,
.port_bridge_join = qca8k_port_bridge_join, .port_bridge_join = qca8k_port_bridge_join,
.port_bridge_leave = qca8k_port_bridge_leave, .port_bridge_leave = qca8k_port_bridge_leave,
.port_fdb_prepare = qca8k_port_fdb_prepare,
.port_fdb_add = qca8k_port_fdb_add, .port_fdb_add = qca8k_port_fdb_add,
.port_fdb_del = qca8k_port_fdb_del, .port_fdb_del = qca8k_port_fdb_del,
.port_fdb_dump = qca8k_port_fdb_dump, .port_fdb_dump = qca8k_port_fdb_dump,
......
...@@ -272,6 +272,8 @@ static inline u8 dsa_upstream_port(struct dsa_switch *ds) ...@@ -272,6 +272,8 @@ static inline u8 dsa_upstream_port(struct dsa_switch *ds)
return ds->rtable[dst->cpu_dp->ds->index]; return ds->rtable[dst->cpu_dp->ds->index];
} }
typedef int dsa_fdb_dump_cb_t(const unsigned char *addr, u16 vid,
bool is_static, void *data);
struct dsa_switch_ops { struct dsa_switch_ops {
/* /*
* Legacy probing. * Legacy probing.
...@@ -378,24 +380,15 @@ struct dsa_switch_ops { ...@@ -378,24 +380,15 @@ struct dsa_switch_ops {
struct switchdev_trans *trans); struct switchdev_trans *trans);
int (*port_vlan_del)(struct dsa_switch *ds, int port, int (*port_vlan_del)(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan); const struct switchdev_obj_port_vlan *vlan);
int (*port_vlan_dump)(struct dsa_switch *ds, int port,
struct switchdev_obj_port_vlan *vlan,
switchdev_obj_dump_cb_t *cb);
/* /*
* Forwarding database * Forwarding database
*/ */
int (*port_fdb_prepare)(struct dsa_switch *ds, int port, int (*port_fdb_add)(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb, const unsigned char *addr, u16 vid);
struct switchdev_trans *trans);
void (*port_fdb_add)(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb,
struct switchdev_trans *trans);
int (*port_fdb_del)(struct dsa_switch *ds, int port, int (*port_fdb_del)(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb); const unsigned char *addr, u16 vid);
int (*port_fdb_dump)(struct dsa_switch *ds, int port, int (*port_fdb_dump)(struct dsa_switch *ds, int port,
struct switchdev_obj_port_fdb *fdb, dsa_fdb_dump_cb_t *cb, void *data);
switchdev_obj_dump_cb_t *cb);
/* /*
* Multicast database * Multicast database
...@@ -408,10 +401,6 @@ struct dsa_switch_ops { ...@@ -408,10 +401,6 @@ struct dsa_switch_ops {
struct switchdev_trans *trans); struct switchdev_trans *trans);
int (*port_mdb_del)(struct dsa_switch *ds, int port, int (*port_mdb_del)(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_mdb *mdb); const struct switchdev_obj_port_mdb *mdb);
int (*port_mdb_dump)(struct dsa_switch *ds, int port,
struct switchdev_obj_port_mdb *mdb,
switchdev_obj_dump_cb_t *cb);
/* /*
* RXNFC * RXNFC
*/ */
......
...@@ -74,7 +74,6 @@ struct switchdev_attr { ...@@ -74,7 +74,6 @@ struct switchdev_attr {
enum switchdev_obj_id { enum switchdev_obj_id {
SWITCHDEV_OBJ_ID_UNDEFINED, SWITCHDEV_OBJ_ID_UNDEFINED,
SWITCHDEV_OBJ_ID_PORT_VLAN, SWITCHDEV_OBJ_ID_PORT_VLAN,
SWITCHDEV_OBJ_ID_PORT_FDB,
SWITCHDEV_OBJ_ID_PORT_MDB, SWITCHDEV_OBJ_ID_PORT_MDB,
}; };
...@@ -97,17 +96,6 @@ struct switchdev_obj_port_vlan { ...@@ -97,17 +96,6 @@ struct switchdev_obj_port_vlan {
#define SWITCHDEV_OBJ_PORT_VLAN(obj) \ #define SWITCHDEV_OBJ_PORT_VLAN(obj) \
container_of(obj, struct switchdev_obj_port_vlan, obj) container_of(obj, struct switchdev_obj_port_vlan, obj)
/* SWITCHDEV_OBJ_ID_PORT_FDB */
struct switchdev_obj_port_fdb {
struct switchdev_obj obj;
unsigned char addr[ETH_ALEN];
u16 vid;
u16 ndm_state;
};
#define SWITCHDEV_OBJ_PORT_FDB(obj) \
container_of(obj, struct switchdev_obj_port_fdb, obj)
/* SWITCHDEV_OBJ_ID_PORT_MDB */ /* SWITCHDEV_OBJ_ID_PORT_MDB */
struct switchdev_obj_port_mdb { struct switchdev_obj_port_mdb {
struct switchdev_obj obj; struct switchdev_obj obj;
...@@ -135,8 +123,6 @@ typedef int switchdev_obj_dump_cb_t(struct switchdev_obj *obj); ...@@ -135,8 +123,6 @@ typedef int switchdev_obj_dump_cb_t(struct switchdev_obj *obj);
* @switchdev_port_obj_add: Add an object to port (see switchdev_obj_*). * @switchdev_port_obj_add: Add an object to port (see switchdev_obj_*).
* *
* @switchdev_port_obj_del: Delete an object from port (see switchdev_obj_*). * @switchdev_port_obj_del: Delete an object from port (see switchdev_obj_*).
*
* @switchdev_port_obj_dump: Dump port objects (see switchdev_obj_*).
*/ */
struct switchdev_ops { struct switchdev_ops {
int (*switchdev_port_attr_get)(struct net_device *dev, int (*switchdev_port_attr_get)(struct net_device *dev,
...@@ -149,9 +135,6 @@ struct switchdev_ops { ...@@ -149,9 +135,6 @@ struct switchdev_ops {
struct switchdev_trans *trans); struct switchdev_trans *trans);
int (*switchdev_port_obj_del)(struct net_device *dev, int (*switchdev_port_obj_del)(struct net_device *dev,
const struct switchdev_obj *obj); const struct switchdev_obj *obj);
int (*switchdev_port_obj_dump)(struct net_device *dev,
struct switchdev_obj *obj,
switchdev_obj_dump_cb_t *cb);
}; };
enum switchdev_notifier_type { enum switchdev_notifier_type {
...@@ -189,28 +172,10 @@ int switchdev_port_obj_add(struct net_device *dev, ...@@ -189,28 +172,10 @@ int switchdev_port_obj_add(struct net_device *dev,
const struct switchdev_obj *obj); const struct switchdev_obj *obj);
int switchdev_port_obj_del(struct net_device *dev, int switchdev_port_obj_del(struct net_device *dev,
const struct switchdev_obj *obj); const struct switchdev_obj *obj);
int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj,
switchdev_obj_dump_cb_t *cb);
int register_switchdev_notifier(struct notifier_block *nb); int register_switchdev_notifier(struct notifier_block *nb);
int unregister_switchdev_notifier(struct notifier_block *nb); int unregister_switchdev_notifier(struct notifier_block *nb);
int call_switchdev_notifiers(unsigned long val, struct net_device *dev, int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
struct switchdev_notifier_info *info); struct switchdev_notifier_info *info);
int switchdev_port_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev, u32 filter_mask,
int nlflags);
int switchdev_port_bridge_setlink(struct net_device *dev,
struct nlmsghdr *nlh, u16 flags);
int switchdev_port_bridge_dellink(struct net_device *dev,
struct nlmsghdr *nlh, u16 flags);
int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, const unsigned char *addr,
u16 vid, u16 nlm_flags);
int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, const unsigned char *addr,
u16 vid);
int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
struct net_device *dev,
struct net_device *filter_dev, int *idx);
void switchdev_port_fwd_mark_set(struct net_device *dev, void switchdev_port_fwd_mark_set(struct net_device *dev,
struct net_device *group_dev, struct net_device *group_dev,
bool joining); bool joining);
...@@ -249,13 +214,6 @@ static inline int switchdev_port_obj_del(struct net_device *dev, ...@@ -249,13 +214,6 @@ static inline int switchdev_port_obj_del(struct net_device *dev,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static inline int switchdev_port_obj_dump(struct net_device *dev,
const struct switchdev_obj *obj,
switchdev_obj_dump_cb_t *cb)
{
return -EOPNOTSUPP;
}
static inline int register_switchdev_notifier(struct notifier_block *nb) static inline int register_switchdev_notifier(struct notifier_block *nb)
{ {
return 0; return 0;
...@@ -273,51 +231,6 @@ static inline int call_switchdev_notifiers(unsigned long val, ...@@ -273,51 +231,6 @@ static inline int call_switchdev_notifiers(unsigned long val,
return NOTIFY_DONE; return NOTIFY_DONE;
} }
static inline int switchdev_port_bridge_getlink(struct sk_buff *skb, u32 pid,
u32 seq, struct net_device *dev,
u32 filter_mask, int nlflags)
{
return -EOPNOTSUPP;
}
static inline int switchdev_port_bridge_setlink(struct net_device *dev,
struct nlmsghdr *nlh,
u16 flags)
{
return -EOPNOTSUPP;
}
static inline int switchdev_port_bridge_dellink(struct net_device *dev,
struct nlmsghdr *nlh,
u16 flags)
{
return -EOPNOTSUPP;
}
static inline int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr,
u16 vid, u16 nlm_flags)
{
return -EOPNOTSUPP;
}
static inline int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid)
{
return -EOPNOTSUPP;
}
static inline int switchdev_port_fdb_dump(struct sk_buff *skb,
struct netlink_callback *cb,
struct net_device *dev,
struct net_device *filter_dev,
int *idx)
{
return *idx;
}
static inline bool switchdev_port_same_parent_id(struct net_device *a, static inline bool switchdev_port_same_parent_id(struct net_device *a,
struct net_device *b) struct net_device *b)
{ {
......
...@@ -169,29 +169,11 @@ static void fdb_del_hw_addr(struct net_bridge *br, const unsigned char *addr) ...@@ -169,29 +169,11 @@ static void fdb_del_hw_addr(struct net_bridge *br, const unsigned char *addr)
} }
} }
static void fdb_del_external_learn(struct net_bridge_fdb_entry *f)
{
struct switchdev_obj_port_fdb fdb = {
.obj = {
.orig_dev = f->dst->dev,
.id = SWITCHDEV_OBJ_ID_PORT_FDB,
.flags = SWITCHDEV_F_DEFER,
},
.vid = f->vlan_id,
};
ether_addr_copy(fdb.addr, f->addr.addr);
switchdev_port_obj_del(f->dst->dev, &fdb.obj);
}
static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f) static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
{ {
if (f->is_static) if (f->is_static)
fdb_del_hw_addr(br, f->addr.addr); fdb_del_hw_addr(br, f->addr.addr);
if (f->added_by_external_learn)
fdb_del_external_learn(f);
hlist_del_init_rcu(&f->hlist); hlist_del_init_rcu(&f->hlist);
fdb_notify(br, f, RTM_DELNEIGH); fdb_notify(br, f, RTM_DELNEIGH);
call_rcu(&f->rcu, fdb_rcu_free); call_rcu(&f->rcu, fdb_rcu_free);
......
...@@ -282,10 +282,22 @@ static struct packet_type dsa_pack_type __read_mostly = { ...@@ -282,10 +282,22 @@ static struct packet_type dsa_pack_type __read_mostly = {
.func = dsa_switch_rcv, .func = dsa_switch_rcv,
}; };
static struct workqueue_struct *dsa_owq;
bool dsa_schedule_work(struct work_struct *work)
{
return queue_work(dsa_owq, work);
}
static int __init dsa_init_module(void) static int __init dsa_init_module(void)
{ {
int rc; int rc;
dsa_owq = alloc_ordered_workqueue("dsa_ordered",
WQ_MEM_RECLAIM);
if (!dsa_owq)
return -ENOMEM;
rc = dsa_slave_register_notifier(); rc = dsa_slave_register_notifier();
if (rc) if (rc)
return rc; return rc;
...@@ -305,6 +317,7 @@ static void __exit dsa_cleanup_module(void) ...@@ -305,6 +317,7 @@ static void __exit dsa_cleanup_module(void)
dsa_slave_unregister_notifier(); dsa_slave_unregister_notifier();
dev_remove_pack(&dsa_pack_type); dev_remove_pack(&dsa_pack_type);
dsa_legacy_unregister(); dsa_legacy_unregister();
destroy_workqueue(dsa_owq);
} }
module_exit(dsa_cleanup_module); module_exit(dsa_cleanup_module);
......
...@@ -43,10 +43,10 @@ struct dsa_notifier_bridge_info { ...@@ -43,10 +43,10 @@ struct dsa_notifier_bridge_info {
/* DSA_NOTIFIER_FDB_* */ /* DSA_NOTIFIER_FDB_* */
struct dsa_notifier_fdb_info { struct dsa_notifier_fdb_info {
const struct switchdev_obj_port_fdb *fdb;
struct switchdev_trans *trans;
int sw_index; int sw_index;
int port; int port;
const unsigned char *addr;
u16 vid;
}; };
/* DSA_NOTIFIER_MDB_* */ /* DSA_NOTIFIER_MDB_* */
...@@ -106,10 +106,18 @@ void dsa_cpu_dsa_destroy(struct dsa_port *dport); ...@@ -106,10 +106,18 @@ void dsa_cpu_dsa_destroy(struct dsa_port *dport);
const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol); const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol);
int dsa_cpu_port_ethtool_setup(struct dsa_port *cpu_dp); int dsa_cpu_port_ethtool_setup(struct dsa_port *cpu_dp);
void dsa_cpu_port_ethtool_restore(struct dsa_port *cpu_dp); void dsa_cpu_port_ethtool_restore(struct dsa_port *cpu_dp);
bool dsa_schedule_work(struct work_struct *work);
/* legacy.c */ /* legacy.c */
int dsa_legacy_register(void); int dsa_legacy_register(void);
void dsa_legacy_unregister(void); void dsa_legacy_unregister(void);
int dsa_legacy_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid,
u16 flags);
int dsa_legacy_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid);
/* port.c */ /* port.c */
int dsa_port_set_state(struct dsa_port *dp, u8 state, int dsa_port_set_state(struct dsa_port *dp, u8 state,
...@@ -121,29 +129,20 @@ int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, ...@@ -121,29 +129,20 @@ int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
struct switchdev_trans *trans); struct switchdev_trans *trans);
int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock, int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock,
struct switchdev_trans *trans); struct switchdev_trans *trans);
int dsa_port_fdb_add(struct dsa_port *dp, int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr,
const struct switchdev_obj_port_fdb *fdb, u16 vid);
struct switchdev_trans *trans); int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr,
int dsa_port_fdb_del(struct dsa_port *dp, u16 vid);
const struct switchdev_obj_port_fdb *fdb);
int dsa_port_fdb_dump(struct dsa_port *dp, struct switchdev_obj_port_fdb *fdb,
switchdev_obj_dump_cb_t *cb);
int dsa_port_mdb_add(struct dsa_port *dp, int dsa_port_mdb_add(struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb, const struct switchdev_obj_port_mdb *mdb,
struct switchdev_trans *trans); struct switchdev_trans *trans);
int dsa_port_mdb_del(struct dsa_port *dp, int dsa_port_mdb_del(struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb); const struct switchdev_obj_port_mdb *mdb);
int dsa_port_mdb_dump(struct dsa_port *dp, struct switchdev_obj_port_mdb *mdb,
switchdev_obj_dump_cb_t *cb);
int dsa_port_vlan_add(struct dsa_port *dp, int dsa_port_vlan_add(struct dsa_port *dp,
const struct switchdev_obj_port_vlan *vlan, const struct switchdev_obj_port_vlan *vlan,
struct switchdev_trans *trans); struct switchdev_trans *trans);
int dsa_port_vlan_del(struct dsa_port *dp, int dsa_port_vlan_del(struct dsa_port *dp,
const struct switchdev_obj_port_vlan *vlan); const struct switchdev_obj_port_vlan *vlan);
int dsa_port_vlan_dump(struct dsa_port *dp,
struct switchdev_obj_port_vlan *vlan,
switchdev_obj_dump_cb_t *cb);
/* slave.c */ /* slave.c */
extern const struct dsa_device_ops notag_netdev_ops; extern const struct dsa_device_ops notag_netdev_ops;
void dsa_slave_mii_bus_init(struct dsa_switch *ds); void dsa_slave_mii_bus_init(struct dsa_switch *ds);
......
...@@ -739,6 +739,28 @@ static int dsa_resume(struct device *d) ...@@ -739,6 +739,28 @@ static int dsa_resume(struct device *d)
} }
#endif #endif
/* legacy way, bypassing the bridge *****************************************/
int dsa_legacy_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid,
u16 flags)
{
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_port *dp = p->dp;
return dsa_port_fdb_add(dp, addr, vid);
}
int dsa_legacy_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid)
{
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_port *dp = p->dp;
return dsa_port_fdb_del(dp, addr, vid);
}
static SIMPLE_DEV_PM_OPS(dsa_pm_ops, dsa_suspend, dsa_resume); static SIMPLE_DEV_PM_OPS(dsa_pm_ops, dsa_suspend, dsa_resume);
static const struct of_device_id dsa_of_match_table[] = { static const struct of_device_id dsa_of_match_table[] = {
......
...@@ -146,43 +146,33 @@ int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock, ...@@ -146,43 +146,33 @@ int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock,
return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
} }
int dsa_port_fdb_add(struct dsa_port *dp, int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr,
const struct switchdev_obj_port_fdb *fdb, u16 vid)
struct switchdev_trans *trans)
{ {
struct dsa_notifier_fdb_info info = { struct dsa_notifier_fdb_info info = {
.sw_index = dp->ds->index, .sw_index = dp->ds->index,
.port = dp->index, .port = dp->index,
.trans = trans, .addr = addr,
.fdb = fdb, .vid = vid,
}; };
return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info); return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info);
} }
int dsa_port_fdb_del(struct dsa_port *dp, int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr,
const struct switchdev_obj_port_fdb *fdb) u16 vid)
{ {
struct dsa_notifier_fdb_info info = { struct dsa_notifier_fdb_info info = {
.sw_index = dp->ds->index, .sw_index = dp->ds->index,
.port = dp->index, .port = dp->index,
.fdb = fdb, .addr = addr,
.vid = vid,
}; };
return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info); return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info);
} }
int dsa_port_fdb_dump(struct dsa_port *dp, struct switchdev_obj_port_fdb *fdb,
switchdev_obj_dump_cb_t *cb)
{
struct dsa_switch *ds = dp->ds;
if (ds->ops->port_fdb_dump)
return ds->ops->port_fdb_dump(ds, dp->index, fdb, cb);
return -EOPNOTSUPP;
}
int dsa_port_mdb_add(struct dsa_port *dp, int dsa_port_mdb_add(struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb, const struct switchdev_obj_port_mdb *mdb,
struct switchdev_trans *trans) struct switchdev_trans *trans)
...@@ -209,17 +199,6 @@ int dsa_port_mdb_del(struct dsa_port *dp, ...@@ -209,17 +199,6 @@ int dsa_port_mdb_del(struct dsa_port *dp,
return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info); return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info);
} }
int dsa_port_mdb_dump(struct dsa_port *dp, struct switchdev_obj_port_mdb *mdb,
switchdev_obj_dump_cb_t *cb)
{
struct dsa_switch *ds = dp->ds;
if (ds->ops->port_mdb_dump)
return ds->ops->port_mdb_dump(ds, dp->index, mdb, cb);
return -EOPNOTSUPP;
}
int dsa_port_vlan_add(struct dsa_port *dp, int dsa_port_vlan_add(struct dsa_port *dp,
const struct switchdev_obj_port_vlan *vlan, const struct switchdev_obj_port_vlan *vlan,
struct switchdev_trans *trans) struct switchdev_trans *trans)
...@@ -245,15 +224,3 @@ int dsa_port_vlan_del(struct dsa_port *dp, ...@@ -245,15 +224,3 @@ int dsa_port_vlan_del(struct dsa_port *dp,
return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info);
} }
int dsa_port_vlan_dump(struct dsa_port *dp,
struct switchdev_obj_port_vlan *vlan,
switchdev_obj_dump_cb_t *cb)
{
struct dsa_switch *ds = dp->ds;
if (ds->ops->port_vlan_dump)
return ds->ops->port_vlan_dump(ds, dp->index, vlan, cb);
return -EOPNOTSUPP;
}
...@@ -199,6 +199,83 @@ static int dsa_slave_set_mac_address(struct net_device *dev, void *a) ...@@ -199,6 +199,83 @@ static int dsa_slave_set_mac_address(struct net_device *dev, void *a)
return 0; return 0;
} }
struct dsa_slave_dump_ctx {
struct net_device *dev;
struct sk_buff *skb;
struct netlink_callback *cb;
int idx;
};
static int
dsa_slave_port_fdb_do_dump(const unsigned char *addr, u16 vid,
bool is_static, void *data)
{
struct dsa_slave_dump_ctx *dump = data;
u32 portid = NETLINK_CB(dump->cb->skb).portid;
u32 seq = dump->cb->nlh->nlmsg_seq;
struct nlmsghdr *nlh;
struct ndmsg *ndm;
if (dump->idx < dump->cb->args[2])
goto skip;
nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
sizeof(*ndm), NLM_F_MULTI);
if (!nlh)
return -EMSGSIZE;
ndm = nlmsg_data(nlh);
ndm->ndm_family = AF_BRIDGE;
ndm->ndm_pad1 = 0;
ndm->ndm_pad2 = 0;
ndm->ndm_flags = NTF_SELF;
ndm->ndm_type = 0;
ndm->ndm_ifindex = dump->dev->ifindex;
ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, addr))
goto nla_put_failure;
if (vid && nla_put_u16(dump->skb, NDA_VLAN, vid))
goto nla_put_failure;
nlmsg_end(dump->skb, nlh);
skip:
dump->idx++;
return 0;
nla_put_failure:
nlmsg_cancel(dump->skb, nlh);
return -EMSGSIZE;
}
static int
dsa_slave_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
struct net_device *dev, struct net_device *filter_dev,
int *idx)
{
struct dsa_slave_dump_ctx dump = {
.dev = dev,
.skb = skb,
.cb = cb,
.idx = *idx,
};
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_port *dp = p->dp;
struct dsa_switch *ds = dp->ds;
int err;
if (!ds->ops->port_fdb_dump)
return -EOPNOTSUPP;
err = ds->ops->port_fdb_dump(ds, dp->index,
dsa_slave_port_fdb_do_dump,
&dump);
*idx = dump.idx;
return err;
}
static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{ {
struct dsa_slave_priv *p = netdev_priv(dev); struct dsa_slave_priv *p = netdev_priv(dev);
...@@ -250,9 +327,6 @@ static int dsa_slave_port_obj_add(struct net_device *dev, ...@@ -250,9 +327,6 @@ static int dsa_slave_port_obj_add(struct net_device *dev,
*/ */
switch (obj->id) { switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_FDB:
err = dsa_port_fdb_add(dp, SWITCHDEV_OBJ_PORT_FDB(obj), trans);
break;
case SWITCHDEV_OBJ_ID_PORT_MDB: case SWITCHDEV_OBJ_ID_PORT_MDB:
err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj), trans); err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj), trans);
break; break;
...@@ -276,9 +350,6 @@ static int dsa_slave_port_obj_del(struct net_device *dev, ...@@ -276,9 +350,6 @@ static int dsa_slave_port_obj_del(struct net_device *dev,
int err; int err;
switch (obj->id) { switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_FDB:
err = dsa_port_fdb_del(dp, SWITCHDEV_OBJ_PORT_FDB(obj));
break;
case SWITCHDEV_OBJ_ID_PORT_MDB: case SWITCHDEV_OBJ_ID_PORT_MDB:
err = dsa_port_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); err = dsa_port_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj));
break; break;
...@@ -293,32 +364,6 @@ static int dsa_slave_port_obj_del(struct net_device *dev, ...@@ -293,32 +364,6 @@ static int dsa_slave_port_obj_del(struct net_device *dev,
return err; return err;
} }
static int dsa_slave_port_obj_dump(struct net_device *dev,
struct switchdev_obj *obj,
switchdev_obj_dump_cb_t *cb)
{
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_port *dp = p->dp;
int err;
switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_FDB:
err = dsa_port_fdb_dump(dp, SWITCHDEV_OBJ_PORT_FDB(obj), cb);
break;
case SWITCHDEV_OBJ_ID_PORT_MDB:
err = dsa_port_mdb_dump(dp, SWITCHDEV_OBJ_PORT_MDB(obj), cb);
break;
case SWITCHDEV_OBJ_ID_PORT_VLAN:
err = dsa_port_vlan_dump(dp, SWITCHDEV_OBJ_PORT_VLAN(obj), cb);
break;
default:
err = -EOPNOTSUPP;
break;
}
return err;
}
static int dsa_slave_port_attr_get(struct net_device *dev, static int dsa_slave_port_attr_get(struct net_device *dev,
struct switchdev_attr *attr) struct switchdev_attr *attr)
{ {
...@@ -330,6 +375,9 @@ static int dsa_slave_port_attr_get(struct net_device *dev, ...@@ -330,6 +375,9 @@ static int dsa_slave_port_attr_get(struct net_device *dev,
attr->u.ppid.id_len = sizeof(ds->index); attr->u.ppid.id_len = sizeof(ds->index);
memcpy(&attr->u.ppid.id, &ds->index, attr->u.ppid.id_len); memcpy(&attr->u.ppid.id, &ds->index, attr->u.ppid.id_len);
break; break;
case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT:
attr->u.brport_flags_support = 0;
break;
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -981,9 +1029,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = { ...@@ -981,9 +1029,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
.ndo_change_rx_flags = dsa_slave_change_rx_flags, .ndo_change_rx_flags = dsa_slave_change_rx_flags,
.ndo_set_rx_mode = dsa_slave_set_rx_mode, .ndo_set_rx_mode = dsa_slave_set_rx_mode,
.ndo_set_mac_address = dsa_slave_set_mac_address, .ndo_set_mac_address = dsa_slave_set_mac_address,
.ndo_fdb_add = switchdev_port_fdb_add, .ndo_fdb_add = dsa_legacy_fdb_add,
.ndo_fdb_del = switchdev_port_fdb_del, .ndo_fdb_del = dsa_legacy_fdb_del,
.ndo_fdb_dump = switchdev_port_fdb_dump, .ndo_fdb_dump = dsa_slave_fdb_dump,
.ndo_do_ioctl = dsa_slave_ioctl, .ndo_do_ioctl = dsa_slave_ioctl,
.ndo_get_iflink = dsa_slave_get_iflink, .ndo_get_iflink = dsa_slave_get_iflink,
#ifdef CONFIG_NET_POLL_CONTROLLER #ifdef CONFIG_NET_POLL_CONTROLLER
...@@ -991,9 +1039,6 @@ static const struct net_device_ops dsa_slave_netdev_ops = { ...@@ -991,9 +1039,6 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
.ndo_netpoll_cleanup = dsa_slave_netpoll_cleanup, .ndo_netpoll_cleanup = dsa_slave_netpoll_cleanup,
.ndo_poll_controller = dsa_slave_poll_controller, .ndo_poll_controller = dsa_slave_poll_controller,
#endif #endif
.ndo_bridge_getlink = switchdev_port_bridge_getlink,
.ndo_bridge_setlink = switchdev_port_bridge_setlink,
.ndo_bridge_dellink = switchdev_port_bridge_dellink,
.ndo_get_phys_port_name = dsa_slave_get_phys_port_name, .ndo_get_phys_port_name = dsa_slave_get_phys_port_name,
.ndo_setup_tc = dsa_slave_setup_tc, .ndo_setup_tc = dsa_slave_setup_tc,
.ndo_get_stats64 = dsa_slave_get_stats64, .ndo_get_stats64 = dsa_slave_get_stats64,
...@@ -1004,7 +1049,6 @@ static const struct switchdev_ops dsa_slave_switchdev_ops = { ...@@ -1004,7 +1049,6 @@ static const struct switchdev_ops dsa_slave_switchdev_ops = {
.switchdev_port_attr_set = dsa_slave_port_attr_set, .switchdev_port_attr_set = dsa_slave_port_attr_set,
.switchdev_port_obj_add = dsa_slave_port_obj_add, .switchdev_port_obj_add = dsa_slave_port_obj_add,
.switchdev_port_obj_del = dsa_slave_port_obj_del, .switchdev_port_obj_del = dsa_slave_port_obj_del,
.switchdev_port_obj_dump = dsa_slave_port_obj_dump,
}; };
static struct device_type dsa_type = { static struct device_type dsa_type = {
...@@ -1328,19 +1372,142 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb, ...@@ -1328,19 +1372,142 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
return NOTIFY_DONE; return NOTIFY_DONE;
} }
struct dsa_switchdev_event_work {
struct work_struct work;
struct switchdev_notifier_fdb_info fdb_info;
struct net_device *dev;
unsigned long event;
};
static void dsa_slave_switchdev_event_work(struct work_struct *work)
{
struct dsa_switchdev_event_work *switchdev_work =
container_of(work, struct dsa_switchdev_event_work, work);
struct net_device *dev = switchdev_work->dev;
struct switchdev_notifier_fdb_info *fdb_info;
struct dsa_slave_priv *p = netdev_priv(dev);
int err;
rtnl_lock();
switch (switchdev_work->event) {
case SWITCHDEV_FDB_ADD_TO_DEVICE:
fdb_info = &switchdev_work->fdb_info;
err = dsa_port_fdb_add(p->dp, fdb_info->addr, fdb_info->vid);
if (err) {
netdev_dbg(dev, "fdb add failed err=%d\n", err);
break;
}
call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev,
&fdb_info->info);
break;
case SWITCHDEV_FDB_DEL_TO_DEVICE:
fdb_info = &switchdev_work->fdb_info;
err = dsa_port_fdb_del(p->dp, fdb_info->addr, fdb_info->vid);
if (err) {
netdev_dbg(dev, "fdb del failed err=%d\n", err);
dev_close(dev);
}
break;
}
rtnl_unlock();
kfree(switchdev_work->fdb_info.addr);
kfree(switchdev_work);
dev_put(dev);
}
static int
dsa_slave_switchdev_fdb_work_init(struct dsa_switchdev_event_work *
switchdev_work,
const struct switchdev_notifier_fdb_info *
fdb_info)
{
memcpy(&switchdev_work->fdb_info, fdb_info,
sizeof(switchdev_work->fdb_info));
switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
if (!switchdev_work->fdb_info.addr)
return -ENOMEM;
ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
fdb_info->addr);
return 0;
}
/* Called under rcu_read_lock() */
static int dsa_slave_switchdev_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
struct dsa_switchdev_event_work *switchdev_work;
if (!dsa_slave_dev_check(dev))
return NOTIFY_DONE;
switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
if (!switchdev_work)
return NOTIFY_BAD;
INIT_WORK(&switchdev_work->work,
dsa_slave_switchdev_event_work);
switchdev_work->dev = dev;
switchdev_work->event = event;
switch (event) {
case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */
case SWITCHDEV_FDB_DEL_TO_DEVICE:
if (dsa_slave_switchdev_fdb_work_init(switchdev_work,
ptr))
goto err_fdb_work_init;
dev_hold(dev);
break;
default:
kfree(switchdev_work);
return NOTIFY_DONE;
}
dsa_schedule_work(&switchdev_work->work);
return NOTIFY_OK;
err_fdb_work_init:
kfree(switchdev_work);
return NOTIFY_BAD;
}
static struct notifier_block dsa_slave_nb __read_mostly = { static struct notifier_block dsa_slave_nb __read_mostly = {
.notifier_call = dsa_slave_netdevice_event, .notifier_call = dsa_slave_netdevice_event,
}; };
static struct notifier_block dsa_slave_switchdev_notifier = {
.notifier_call = dsa_slave_switchdev_event,
};
int dsa_slave_register_notifier(void) int dsa_slave_register_notifier(void)
{ {
return register_netdevice_notifier(&dsa_slave_nb); int err;
err = register_netdevice_notifier(&dsa_slave_nb);
if (err)
return err;
err = register_switchdev_notifier(&dsa_slave_switchdev_notifier);
if (err)
goto err_switchdev_nb;
return 0;
err_switchdev_nb:
unregister_netdevice_notifier(&dsa_slave_nb);
return err;
} }
void dsa_slave_unregister_notifier(void) void dsa_slave_unregister_notifier(void)
{ {
int err; int err;
err = unregister_switchdev_notifier(&dsa_slave_switchdev_notifier);
if (err)
pr_err("DSA: failed to unregister switchdev notifier (%d)\n", err);
err = unregister_netdevice_notifier(&dsa_slave_nb); err = unregister_netdevice_notifier(&dsa_slave_nb);
if (err) if (err)
pr_err("DSA: failed to unregister slave notifier (%d)\n", err); pr_err("DSA: failed to unregister slave notifier (%d)\n", err);
......
...@@ -83,30 +83,20 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds, ...@@ -83,30 +83,20 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds,
static int dsa_switch_fdb_add(struct dsa_switch *ds, static int dsa_switch_fdb_add(struct dsa_switch *ds,
struct dsa_notifier_fdb_info *info) struct dsa_notifier_fdb_info *info)
{ {
const struct switchdev_obj_port_fdb *fdb = info->fdb;
struct switchdev_trans *trans = info->trans;
/* Do not care yet about other switch chips of the fabric */ /* Do not care yet about other switch chips of the fabric */
if (ds->index != info->sw_index) if (ds->index != info->sw_index)
return 0; return 0;
if (switchdev_trans_ph_prepare(trans)) { if (!ds->ops->port_fdb_add)
if (!ds->ops->port_fdb_prepare || !ds->ops->port_fdb_add)
return -EOPNOTSUPP; return -EOPNOTSUPP;
return ds->ops->port_fdb_prepare(ds, info->port, fdb, trans); return ds->ops->port_fdb_add(ds, info->port, info->addr,
} info->vid);
ds->ops->port_fdb_add(ds, info->port, fdb, trans);
return 0;
} }
static int dsa_switch_fdb_del(struct dsa_switch *ds, static int dsa_switch_fdb_del(struct dsa_switch *ds,
struct dsa_notifier_fdb_info *info) struct dsa_notifier_fdb_info *info)
{ {
const struct switchdev_obj_port_fdb *fdb = info->fdb;
/* Do not care yet about other switch chips of the fabric */ /* Do not care yet about other switch chips of the fabric */
if (ds->index != info->sw_index) if (ds->index != info->sw_index)
return 0; return 0;
...@@ -114,7 +104,8 @@ static int dsa_switch_fdb_del(struct dsa_switch *ds, ...@@ -114,7 +104,8 @@ static int dsa_switch_fdb_del(struct dsa_switch *ds,
if (!ds->ops->port_fdb_del) if (!ds->ops->port_fdb_del)
return -EOPNOTSUPP; return -EOPNOTSUPP;
return ds->ops->port_fdb_del(ds, info->port, fdb); return ds->ops->port_fdb_del(ds, info->port, info->addr,
info->vid);
} }
static int dsa_switch_mdb_add(struct dsa_switch *ds, static int dsa_switch_mdb_add(struct dsa_switch *ds,
......
...@@ -343,8 +343,6 @@ static size_t switchdev_obj_size(const struct switchdev_obj *obj) ...@@ -343,8 +343,6 @@ static size_t switchdev_obj_size(const struct switchdev_obj *obj)
switch (obj->id) { switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_VLAN: case SWITCHDEV_OBJ_ID_PORT_VLAN:
return sizeof(struct switchdev_obj_port_vlan); return sizeof(struct switchdev_obj_port_vlan);
case SWITCHDEV_OBJ_ID_PORT_FDB:
return sizeof(struct switchdev_obj_port_fdb);
case SWITCHDEV_OBJ_ID_PORT_MDB: case SWITCHDEV_OBJ_ID_PORT_MDB:
return sizeof(struct switchdev_obj_port_mdb); return sizeof(struct switchdev_obj_port_mdb);
default: default:
...@@ -534,43 +532,6 @@ int switchdev_port_obj_del(struct net_device *dev, ...@@ -534,43 +532,6 @@ int switchdev_port_obj_del(struct net_device *dev,
} }
EXPORT_SYMBOL_GPL(switchdev_port_obj_del); EXPORT_SYMBOL_GPL(switchdev_port_obj_del);
/**
* switchdev_port_obj_dump - Dump port objects
*
* @dev: port device
* @id: object ID
* @obj: object to dump
* @cb: function to call with a filled object
*
* rtnl_lock must be held.
*/
int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj,
switchdev_obj_dump_cb_t *cb)
{
const struct switchdev_ops *ops = dev->switchdev_ops;
struct net_device *lower_dev;
struct list_head *iter;
int err = -EOPNOTSUPP;
ASSERT_RTNL();
if (ops && ops->switchdev_port_obj_dump)
return ops->switchdev_port_obj_dump(dev, obj, cb);
/* Switch device port(s) may be stacked under
* bond/team/vlan dev, so recurse down to dump objects on
* first port at bottom of stack.
*/
netdev_for_each_lower_dev(dev, lower_dev, iter) {
err = switchdev_port_obj_dump(lower_dev, obj, cb);
break;
}
return err;
}
EXPORT_SYMBOL_GPL(switchdev_port_obj_dump);
static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain); static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain);
/** /**
...@@ -613,486 +574,6 @@ int call_switchdev_notifiers(unsigned long val, struct net_device *dev, ...@@ -613,486 +574,6 @@ int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
} }
EXPORT_SYMBOL_GPL(call_switchdev_notifiers); EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
struct switchdev_vlan_dump {
struct switchdev_obj_port_vlan vlan;
struct sk_buff *skb;
u32 filter_mask;
u16 flags;
u16 begin;
u16 end;
};
static int switchdev_port_vlan_dump_put(struct switchdev_vlan_dump *dump)
{
struct bridge_vlan_info vinfo;
vinfo.flags = dump->flags;
if (dump->begin == 0 && dump->end == 0) {
return 0;
} else if (dump->begin == dump->end) {
vinfo.vid = dump->begin;
if (nla_put(dump->skb, IFLA_BRIDGE_VLAN_INFO,
sizeof(vinfo), &vinfo))
return -EMSGSIZE;
} else {
vinfo.vid = dump->begin;
vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
if (nla_put(dump->skb, IFLA_BRIDGE_VLAN_INFO,
sizeof(vinfo), &vinfo))
return -EMSGSIZE;
vinfo.vid = dump->end;
vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END;
if (nla_put(dump->skb, IFLA_BRIDGE_VLAN_INFO,
sizeof(vinfo), &vinfo))
return -EMSGSIZE;
}
return 0;
}
static int switchdev_port_vlan_dump_cb(struct switchdev_obj *obj)
{
struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
struct switchdev_vlan_dump *dump =
container_of(vlan, struct switchdev_vlan_dump, vlan);
int err = 0;
if (vlan->vid_begin > vlan->vid_end)
return -EINVAL;
if (dump->filter_mask & RTEXT_FILTER_BRVLAN) {
dump->flags = vlan->flags;
for (dump->begin = dump->end = vlan->vid_begin;
dump->begin <= vlan->vid_end;
dump->begin++, dump->end++) {
err = switchdev_port_vlan_dump_put(dump);
if (err)
return err;
}
} else if (dump->filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED) {
if (dump->begin > vlan->vid_begin &&
dump->begin >= vlan->vid_end) {
if ((dump->begin - 1) == vlan->vid_end &&
dump->flags == vlan->flags) {
/* prepend */
dump->begin = vlan->vid_begin;
} else {
err = switchdev_port_vlan_dump_put(dump);
dump->flags = vlan->flags;
dump->begin = vlan->vid_begin;
dump->end = vlan->vid_end;
}
} else if (dump->end <= vlan->vid_begin &&
dump->end < vlan->vid_end) {
if ((dump->end + 1) == vlan->vid_begin &&
dump->flags == vlan->flags) {
/* append */
dump->end = vlan->vid_end;
} else {
err = switchdev_port_vlan_dump_put(dump);
dump->flags = vlan->flags;
dump->begin = vlan->vid_begin;
dump->end = vlan->vid_end;
}
} else {
err = -EINVAL;
}
}
return err;
}
static int switchdev_port_vlan_fill(struct sk_buff *skb, struct net_device *dev,
u32 filter_mask)
{
struct switchdev_vlan_dump dump = {
.vlan.obj.orig_dev = dev,
.vlan.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.skb = skb,
.filter_mask = filter_mask,
};
int err = 0;
if ((filter_mask & RTEXT_FILTER_BRVLAN) ||
(filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)) {
err = switchdev_port_obj_dump(dev, &dump.vlan.obj,
switchdev_port_vlan_dump_cb);
if (err)
goto err_out;
if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)
/* last one */
err = switchdev_port_vlan_dump_put(&dump);
}
err_out:
return err == -EOPNOTSUPP ? 0 : err;
}
/**
* switchdev_port_bridge_getlink - Get bridge port attributes
*
* @dev: port device
*
* Called for SELF on rtnl_bridge_getlink to get bridge port
* attributes.
*/
int switchdev_port_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev, u32 filter_mask,
int nlflags)
{
struct switchdev_attr attr = {
.orig_dev = dev,
.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
};
u16 mode = BRIDGE_MODE_UNDEF;
u32 mask = BR_LEARNING | BR_LEARNING_SYNC | BR_FLOOD;
int err;
if (!netif_is_bridge_port(dev))
return -EOPNOTSUPP;
err = switchdev_port_attr_get(dev, &attr);
if (err && err != -EOPNOTSUPP)
return err;
return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode,
attr.u.brport_flags, mask, nlflags,
filter_mask, switchdev_port_vlan_fill);
}
EXPORT_SYMBOL_GPL(switchdev_port_bridge_getlink);
static int switchdev_port_br_setflag(struct net_device *dev,
struct nlattr *nlattr,
unsigned long brport_flag)
{
struct switchdev_attr attr = {
.orig_dev = dev,
.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
};
u8 flag = nla_get_u8(nlattr);
int err;
err = switchdev_port_attr_get(dev, &attr);
if (err)
return err;
if (flag)
attr.u.brport_flags |= brport_flag;
else
attr.u.brport_flags &= ~brport_flag;
return switchdev_port_attr_set(dev, &attr);
}
static const struct nla_policy
switchdev_port_bridge_policy[IFLA_BRPORT_MAX + 1] = {
[IFLA_BRPORT_STATE] = { .type = NLA_U8 },
[IFLA_BRPORT_COST] = { .type = NLA_U32 },
[IFLA_BRPORT_PRIORITY] = { .type = NLA_U16 },
[IFLA_BRPORT_MODE] = { .type = NLA_U8 },
[IFLA_BRPORT_GUARD] = { .type = NLA_U8 },
[IFLA_BRPORT_PROTECT] = { .type = NLA_U8 },
[IFLA_BRPORT_FAST_LEAVE] = { .type = NLA_U8 },
[IFLA_BRPORT_LEARNING] = { .type = NLA_U8 },
[IFLA_BRPORT_LEARNING_SYNC] = { .type = NLA_U8 },
[IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 },
};
static int switchdev_port_br_setlink_protinfo(struct net_device *dev,
struct nlattr *protinfo)
{
struct nlattr *attr;
int rem;
int err;
err = nla_validate_nested(protinfo, IFLA_BRPORT_MAX,
switchdev_port_bridge_policy, NULL);
if (err)
return err;
nla_for_each_nested(attr, protinfo, rem) {
switch (nla_type(attr)) {
case IFLA_BRPORT_LEARNING:
err = switchdev_port_br_setflag(dev, attr,
BR_LEARNING);
break;
case IFLA_BRPORT_LEARNING_SYNC:
err = switchdev_port_br_setflag(dev, attr,
BR_LEARNING_SYNC);
break;
case IFLA_BRPORT_UNICAST_FLOOD:
err = switchdev_port_br_setflag(dev, attr, BR_FLOOD);
break;
default:
err = -EOPNOTSUPP;
break;
}
if (err)
return err;
}
return 0;
}
static int switchdev_port_br_afspec(struct net_device *dev,
struct nlattr *afspec,
int (*f)(struct net_device *dev,
const struct switchdev_obj *obj))
{
struct nlattr *attr;
struct bridge_vlan_info *vinfo;
struct switchdev_obj_port_vlan vlan = {
.obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
};
int rem;
int err;
nla_for_each_nested(attr, afspec, rem) {
if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO)
continue;
if (nla_len(attr) != sizeof(struct bridge_vlan_info))
return -EINVAL;
vinfo = nla_data(attr);
if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK)
return -EINVAL;
vlan.flags = vinfo->flags;
if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
if (vlan.vid_begin)
return -EINVAL;
vlan.vid_begin = vinfo->vid;
/* don't allow range of pvids */
if (vlan.flags & BRIDGE_VLAN_INFO_PVID)
return -EINVAL;
} else if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END) {
if (!vlan.vid_begin)
return -EINVAL;
vlan.vid_end = vinfo->vid;
if (vlan.vid_end <= vlan.vid_begin)
return -EINVAL;
err = f(dev, &vlan.obj);
if (err)
return err;
vlan.vid_begin = 0;
} else {
if (vlan.vid_begin)
return -EINVAL;
vlan.vid_begin = vinfo->vid;
vlan.vid_end = vinfo->vid;
err = f(dev, &vlan.obj);
if (err)
return err;
vlan.vid_begin = 0;
}
}
return 0;
}
/**
* switchdev_port_bridge_setlink - Set bridge port attributes
*
* @dev: port device
* @nlh: netlink header
* @flags: netlink flags
*
* Called for SELF on rtnl_bridge_setlink to set bridge port
* attributes.
*/
int switchdev_port_bridge_setlink(struct net_device *dev,
struct nlmsghdr *nlh, u16 flags)
{
struct nlattr *protinfo;
struct nlattr *afspec;
int err = 0;
if (!netif_is_bridge_port(dev))
return -EOPNOTSUPP;
protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg),
IFLA_PROTINFO);
if (protinfo) {
err = switchdev_port_br_setlink_protinfo(dev, protinfo);
if (err)
return err;
}
afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg),
IFLA_AF_SPEC);
if (afspec)
err = switchdev_port_br_afspec(dev, afspec,
switchdev_port_obj_add);
return err;
}
EXPORT_SYMBOL_GPL(switchdev_port_bridge_setlink);
/**
* switchdev_port_bridge_dellink - Set bridge port attributes
*
* @dev: port device
* @nlh: netlink header
* @flags: netlink flags
*
* Called for SELF on rtnl_bridge_dellink to set bridge port
* attributes.
*/
int switchdev_port_bridge_dellink(struct net_device *dev,
struct nlmsghdr *nlh, u16 flags)
{
struct nlattr *afspec;
if (!netif_is_bridge_port(dev))
return -EOPNOTSUPP;
afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg),
IFLA_AF_SPEC);
if (afspec)
return switchdev_port_br_afspec(dev, afspec,
switchdev_port_obj_del);
return 0;
}
EXPORT_SYMBOL_GPL(switchdev_port_bridge_dellink);
/**
* switchdev_port_fdb_add - Add FDB (MAC/VLAN) entry to port
*
* @ndmsg: netlink hdr
* @nlattr: netlink attributes
* @dev: port device
* @addr: MAC address to add
* @vid: VLAN to add
*
* Add FDB entry to switch device.
*/
int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, const unsigned char *addr,
u16 vid, u16 nlm_flags)
{
struct switchdev_obj_port_fdb fdb = {
.obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_FDB,
.vid = vid,
};
ether_addr_copy(fdb.addr, addr);
return switchdev_port_obj_add(dev, &fdb.obj);
}
EXPORT_SYMBOL_GPL(switchdev_port_fdb_add);
/**
* switchdev_port_fdb_del - Delete FDB (MAC/VLAN) entry from port
*
* @ndmsg: netlink hdr
* @nlattr: netlink attributes
* @dev: port device
* @addr: MAC address to delete
* @vid: VLAN to delete
*
* Delete FDB entry from switch device.
*/
int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, const unsigned char *addr,
u16 vid)
{
struct switchdev_obj_port_fdb fdb = {
.obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_FDB,
.vid = vid,
};
ether_addr_copy(fdb.addr, addr);
return switchdev_port_obj_del(dev, &fdb.obj);
}
EXPORT_SYMBOL_GPL(switchdev_port_fdb_del);
struct switchdev_fdb_dump {
struct switchdev_obj_port_fdb fdb;
struct net_device *dev;
struct sk_buff *skb;
struct netlink_callback *cb;
int idx;
};
static int switchdev_port_fdb_dump_cb(struct switchdev_obj *obj)
{
struct switchdev_obj_port_fdb *fdb = SWITCHDEV_OBJ_PORT_FDB(obj);
struct switchdev_fdb_dump *dump =
container_of(fdb, struct switchdev_fdb_dump, fdb);
u32 portid = NETLINK_CB(dump->cb->skb).portid;
u32 seq = dump->cb->nlh->nlmsg_seq;
struct nlmsghdr *nlh;
struct ndmsg *ndm;
if (dump->idx < dump->cb->args[2])
goto skip;
nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
sizeof(*ndm), NLM_F_MULTI);
if (!nlh)
return -EMSGSIZE;
ndm = nlmsg_data(nlh);
ndm->ndm_family = AF_BRIDGE;
ndm->ndm_pad1 = 0;
ndm->ndm_pad2 = 0;
ndm->ndm_flags = NTF_SELF;
ndm->ndm_type = 0;
ndm->ndm_ifindex = dump->dev->ifindex;
ndm->ndm_state = fdb->ndm_state;
if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, fdb->addr))
goto nla_put_failure;
if (fdb->vid && nla_put_u16(dump->skb, NDA_VLAN, fdb->vid))
goto nla_put_failure;
nlmsg_end(dump->skb, nlh);
skip:
dump->idx++;
return 0;
nla_put_failure:
nlmsg_cancel(dump->skb, nlh);
return -EMSGSIZE;
}
/**
* switchdev_port_fdb_dump - Dump port FDB (MAC/VLAN) entries
*
* @skb: netlink skb
* @cb: netlink callback
* @dev: port device
* @filter_dev: filter device
* @idx:
*
* Dump FDB entries from switch device.
*/
int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
struct net_device *dev,
struct net_device *filter_dev, int *idx)
{
struct switchdev_fdb_dump dump = {
.fdb.obj.orig_dev = dev,
.fdb.obj.id = SWITCHDEV_OBJ_ID_PORT_FDB,
.dev = dev,
.skb = skb,
.cb = cb,
.idx = *idx,
};
int err;
err = switchdev_port_obj_dump(dev, &dump.fdb.obj,
switchdev_port_fdb_dump_cb);
*idx = dump.idx;
return err;
}
EXPORT_SYMBOL_GPL(switchdev_port_fdb_dump);
bool switchdev_port_same_parent_id(struct net_device *a, bool switchdev_port_same_parent_id(struct net_device *a,
struct net_device *b) struct net_device *b)
{ {
......
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