mirror of https://gitee.com/openkylin/linux.git
net: dsa: reference count the FDB addresses at the cross-chip notifier level
The same concerns expressed for host MDB entries are valid for host FDBs just as well: - in the case of multiple bridges spanning the same switch chip, deleting a host FDB entry that belongs to one bridge will result in breakage to the other bridge - not deleting FDB entries across DSA links means that the switch's hardware tables will eventually run out, given enough wear&tear So do the same thing and introduce reference counting for CPU ports and DSA links using the same data structures as we have for MDB entries. Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
3dc80afc50
commit
3f6e32f92a
|
@ -288,6 +288,7 @@ struct dsa_port {
|
|||
/* List of MAC addresses that must be forwarded on this port.
|
||||
* These are only valid on CPU ports and DSA links.
|
||||
*/
|
||||
struct list_head fdbs;
|
||||
struct list_head mdbs;
|
||||
|
||||
bool setup;
|
||||
|
|
|
@ -348,6 +348,7 @@ static int dsa_port_setup(struct dsa_port *dp)
|
|||
if (dp->setup)
|
||||
return 0;
|
||||
|
||||
INIT_LIST_HEAD(&dp->fdbs);
|
||||
INIT_LIST_HEAD(&dp->mdbs);
|
||||
|
||||
switch (dp->type) {
|
||||
|
@ -471,6 +472,11 @@ static void dsa_port_teardown(struct dsa_port *dp)
|
|||
break;
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(a, tmp, &dp->fdbs, list) {
|
||||
list_del(&a->list);
|
||||
kfree(a);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(a, tmp, &dp->mdbs, list) {
|
||||
list_del(&a->list);
|
||||
kfree(a);
|
||||
|
|
|
@ -253,6 +253,71 @@ static int dsa_switch_do_mdb_del(struct dsa_switch *ds, int port,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dsa_switch_do_fdb_add(struct dsa_switch *ds, int port,
|
||||
const unsigned char *addr, u16 vid)
|
||||
{
|
||||
struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
struct dsa_mac_addr *a;
|
||||
int err;
|
||||
|
||||
/* No need to bother with refcounting for user ports */
|
||||
if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
|
||||
return ds->ops->port_fdb_add(ds, port, addr, vid);
|
||||
|
||||
a = dsa_mac_addr_find(&dp->fdbs, addr, vid);
|
||||
if (a) {
|
||||
refcount_inc(&a->refcount);
|
||||
return 0;
|
||||
}
|
||||
|
||||
a = kzalloc(sizeof(*a), GFP_KERNEL);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
err = ds->ops->port_fdb_add(ds, port, addr, vid);
|
||||
if (err) {
|
||||
kfree(a);
|
||||
return err;
|
||||
}
|
||||
|
||||
ether_addr_copy(a->addr, addr);
|
||||
a->vid = vid;
|
||||
refcount_set(&a->refcount, 1);
|
||||
list_add_tail(&a->list, &dp->fdbs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsa_switch_do_fdb_del(struct dsa_switch *ds, int port,
|
||||
const unsigned char *addr, u16 vid)
|
||||
{
|
||||
struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
struct dsa_mac_addr *a;
|
||||
int err;
|
||||
|
||||
/* No need to bother with refcounting for user ports */
|
||||
if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
|
||||
return ds->ops->port_fdb_del(ds, port, addr, vid);
|
||||
|
||||
a = dsa_mac_addr_find(&dp->fdbs, addr, vid);
|
||||
if (!a)
|
||||
return -ENOENT;
|
||||
|
||||
if (!refcount_dec_and_test(&a->refcount))
|
||||
return 0;
|
||||
|
||||
err = ds->ops->port_fdb_del(ds, port, addr, vid);
|
||||
if (err) {
|
||||
refcount_inc(&a->refcount);
|
||||
return err;
|
||||
}
|
||||
|
||||
list_del(&a->list);
|
||||
kfree(a);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsa_switch_host_fdb_add(struct dsa_switch *ds,
|
||||
struct dsa_notifier_fdb_info *info)
|
||||
{
|
||||
|
@ -265,7 +330,7 @@ static int dsa_switch_host_fdb_add(struct dsa_switch *ds,
|
|||
for (port = 0; port < ds->num_ports; port++) {
|
||||
if (dsa_switch_host_address_match(ds, port, info->sw_index,
|
||||
info->port)) {
|
||||
err = ds->ops->port_fdb_add(ds, port, info->addr,
|
||||
err = dsa_switch_do_fdb_add(ds, port, info->addr,
|
||||
info->vid);
|
||||
if (err)
|
||||
break;
|
||||
|
@ -278,14 +343,23 @@ static int dsa_switch_host_fdb_add(struct dsa_switch *ds,
|
|||
static int dsa_switch_host_fdb_del(struct dsa_switch *ds,
|
||||
struct dsa_notifier_fdb_info *info)
|
||||
{
|
||||
int err = 0;
|
||||
int port;
|
||||
|
||||
if (!ds->ops->port_fdb_del)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (ds->index == info->sw_index)
|
||||
return ds->ops->port_fdb_del(ds, info->port, info->addr,
|
||||
info->vid);
|
||||
for (port = 0; port < ds->num_ports; port++) {
|
||||
if (dsa_switch_host_address_match(ds, port, info->sw_index,
|
||||
info->port)) {
|
||||
err = dsa_switch_do_fdb_del(ds, port, info->addr,
|
||||
info->vid);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dsa_switch_fdb_add(struct dsa_switch *ds,
|
||||
|
@ -296,7 +370,7 @@ static int dsa_switch_fdb_add(struct dsa_switch *ds,
|
|||
if (!ds->ops->port_fdb_add)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return ds->ops->port_fdb_add(ds, port, info->addr, info->vid);
|
||||
return dsa_switch_do_fdb_add(ds, port, info->addr, info->vid);
|
||||
}
|
||||
|
||||
static int dsa_switch_fdb_del(struct dsa_switch *ds,
|
||||
|
@ -307,7 +381,7 @@ static int dsa_switch_fdb_del(struct dsa_switch *ds,
|
|||
if (!ds->ops->port_fdb_del)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return ds->ops->port_fdb_del(ds, port, info->addr, info->vid);
|
||||
return dsa_switch_do_fdb_del(ds, port, info->addr, info->vid);
|
||||
}
|
||||
|
||||
static int dsa_switch_hsr_join(struct dsa_switch *ds,
|
||||
|
|
Loading…
Reference in New Issue