Skip to content

Commit 3f6e32f

Browse files
vladimirolteandavem330
authored andcommitted
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 <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 3dc80af commit 3f6e32f

File tree

3 files changed

+88
-7
lines changed

3 files changed

+88
-7
lines changed

include/net/dsa.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ struct dsa_port {
288288
/* List of MAC addresses that must be forwarded on this port.
289289
* These are only valid on CPU ports and DSA links.
290290
*/
291+
struct list_head fdbs;
291292
struct list_head mdbs;
292293

293294
bool setup;

net/dsa/dsa2.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ static int dsa_port_setup(struct dsa_port *dp)
348348
if (dp->setup)
349349
return 0;
350350

351+
INIT_LIST_HEAD(&dp->fdbs);
351352
INIT_LIST_HEAD(&dp->mdbs);
352353

353354
switch (dp->type) {
@@ -471,6 +472,11 @@ static void dsa_port_teardown(struct dsa_port *dp)
471472
break;
472473
}
473474

475+
list_for_each_entry_safe(a, tmp, &dp->fdbs, list) {
476+
list_del(&a->list);
477+
kfree(a);
478+
}
479+
474480
list_for_each_entry_safe(a, tmp, &dp->mdbs, list) {
475481
list_del(&a->list);
476482
kfree(a);

net/dsa/switch.c

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,71 @@ static int dsa_switch_do_mdb_del(struct dsa_switch *ds, int port,
253253
return 0;
254254
}
255255

256+
static int dsa_switch_do_fdb_add(struct dsa_switch *ds, int port,
257+
const unsigned char *addr, u16 vid)
258+
{
259+
struct dsa_port *dp = dsa_to_port(ds, port);
260+
struct dsa_mac_addr *a;
261+
int err;
262+
263+
/* No need to bother with refcounting for user ports */
264+
if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
265+
return ds->ops->port_fdb_add(ds, port, addr, vid);
266+
267+
a = dsa_mac_addr_find(&dp->fdbs, addr, vid);
268+
if (a) {
269+
refcount_inc(&a->refcount);
270+
return 0;
271+
}
272+
273+
a = kzalloc(sizeof(*a), GFP_KERNEL);
274+
if (!a)
275+
return -ENOMEM;
276+
277+
err = ds->ops->port_fdb_add(ds, port, addr, vid);
278+
if (err) {
279+
kfree(a);
280+
return err;
281+
}
282+
283+
ether_addr_copy(a->addr, addr);
284+
a->vid = vid;
285+
refcount_set(&a->refcount, 1);
286+
list_add_tail(&a->list, &dp->fdbs);
287+
288+
return 0;
289+
}
290+
291+
static int dsa_switch_do_fdb_del(struct dsa_switch *ds, int port,
292+
const unsigned char *addr, u16 vid)
293+
{
294+
struct dsa_port *dp = dsa_to_port(ds, port);
295+
struct dsa_mac_addr *a;
296+
int err;
297+
298+
/* No need to bother with refcounting for user ports */
299+
if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
300+
return ds->ops->port_fdb_del(ds, port, addr, vid);
301+
302+
a = dsa_mac_addr_find(&dp->fdbs, addr, vid);
303+
if (!a)
304+
return -ENOENT;
305+
306+
if (!refcount_dec_and_test(&a->refcount))
307+
return 0;
308+
309+
err = ds->ops->port_fdb_del(ds, port, addr, vid);
310+
if (err) {
311+
refcount_inc(&a->refcount);
312+
return err;
313+
}
314+
315+
list_del(&a->list);
316+
kfree(a);
317+
318+
return 0;
319+
}
320+
256321
static int dsa_switch_host_fdb_add(struct dsa_switch *ds,
257322
struct dsa_notifier_fdb_info *info)
258323
{
@@ -265,7 +330,7 @@ static int dsa_switch_host_fdb_add(struct dsa_switch *ds,
265330
for (port = 0; port < ds->num_ports; port++) {
266331
if (dsa_switch_host_address_match(ds, port, info->sw_index,
267332
info->port)) {
268-
err = ds->ops->port_fdb_add(ds, port, info->addr,
333+
err = dsa_switch_do_fdb_add(ds, port, info->addr,
269334
info->vid);
270335
if (err)
271336
break;
@@ -278,14 +343,23 @@ static int dsa_switch_host_fdb_add(struct dsa_switch *ds,
278343
static int dsa_switch_host_fdb_del(struct dsa_switch *ds,
279344
struct dsa_notifier_fdb_info *info)
280345
{
346+
int err = 0;
347+
int port;
348+
281349
if (!ds->ops->port_fdb_del)
282350
return -EOPNOTSUPP;
283351

284-
if (ds->index == info->sw_index)
285-
return ds->ops->port_fdb_del(ds, info->port, info->addr,
286-
info->vid);
352+
for (port = 0; port < ds->num_ports; port++) {
353+
if (dsa_switch_host_address_match(ds, port, info->sw_index,
354+
info->port)) {
355+
err = dsa_switch_do_fdb_del(ds, port, info->addr,
356+
info->vid);
357+
if (err)
358+
break;
359+
}
360+
}
287361

288-
return 0;
362+
return err;
289363
}
290364

291365
static int dsa_switch_fdb_add(struct dsa_switch *ds,
@@ -296,7 +370,7 @@ static int dsa_switch_fdb_add(struct dsa_switch *ds,
296370
if (!ds->ops->port_fdb_add)
297371
return -EOPNOTSUPP;
298372

299-
return ds->ops->port_fdb_add(ds, port, info->addr, info->vid);
373+
return dsa_switch_do_fdb_add(ds, port, info->addr, info->vid);
300374
}
301375

302376
static int dsa_switch_fdb_del(struct dsa_switch *ds,
@@ -307,7 +381,7 @@ static int dsa_switch_fdb_del(struct dsa_switch *ds,
307381
if (!ds->ops->port_fdb_del)
308382
return -EOPNOTSUPP;
309383

310-
return ds->ops->port_fdb_del(ds, port, info->addr, info->vid);
384+
return dsa_switch_do_fdb_del(ds, port, info->addr, info->vid);
311385
}
312386

313387
static int dsa_switch_hsr_join(struct dsa_switch *ds,

0 commit comments

Comments
 (0)