Skip to content

Commit c9eb3e0

Browse files
Arkadi Sharshevskydavem330
authored andcommitted
net: dsa: Add support for learning FDB through notification
Add support for learning FDB through notification. The driver defers the hardware update via ordered work queue. In case of a successful FDB add a notification is sent back to bridge. In case of hw FDB del failure the static FDB will be deleted from the bridge, thus, the interface is moved to down state in order to indicate inconsistent situation. Signed-off-by: Arkadi Sharshevsky <[email protected]> Reviewed-by: Florian Fainelli <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 2acf4e6 commit c9eb3e0

File tree

3 files changed

+139
-2
lines changed

3 files changed

+139
-2
lines changed

net/dsa/dsa.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,10 +282,22 @@ static struct packet_type dsa_pack_type __read_mostly = {
282282
.func = dsa_switch_rcv,
283283
};
284284

285+
static struct workqueue_struct *dsa_owq;
286+
287+
bool dsa_schedule_work(struct work_struct *work)
288+
{
289+
return queue_work(dsa_owq, work);
290+
}
291+
285292
static int __init dsa_init_module(void)
286293
{
287294
int rc;
288295

296+
dsa_owq = alloc_ordered_workqueue("dsa_ordered",
297+
WQ_MEM_RECLAIM);
298+
if (!dsa_owq)
299+
return -ENOMEM;
300+
289301
rc = dsa_slave_register_notifier();
290302
if (rc)
291303
return rc;
@@ -305,6 +317,7 @@ static void __exit dsa_cleanup_module(void)
305317
dsa_slave_unregister_notifier();
306318
dev_remove_pack(&dsa_pack_type);
307319
dsa_legacy_unregister();
320+
destroy_workqueue(dsa_owq);
308321
}
309322
module_exit(dsa_cleanup_module);
310323

net/dsa/dsa_priv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ void dsa_cpu_dsa_destroy(struct dsa_port *dport);
106106
const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol);
107107
int dsa_cpu_port_ethtool_setup(struct dsa_port *cpu_dp);
108108
void dsa_cpu_port_ethtool_restore(struct dsa_port *cpu_dp);
109+
bool dsa_schedule_work(struct work_struct *work);
109110

110111
/* legacy.c */
111112
int dsa_legacy_register(void);

net/dsa/slave.c

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,19 +1332,142 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
13321332
return NOTIFY_DONE;
13331333
}
13341334

1335+
struct dsa_switchdev_event_work {
1336+
struct work_struct work;
1337+
struct switchdev_notifier_fdb_info fdb_info;
1338+
struct net_device *dev;
1339+
unsigned long event;
1340+
};
1341+
1342+
static void dsa_slave_switchdev_event_work(struct work_struct *work)
1343+
{
1344+
struct dsa_switchdev_event_work *switchdev_work =
1345+
container_of(work, struct dsa_switchdev_event_work, work);
1346+
struct net_device *dev = switchdev_work->dev;
1347+
struct switchdev_notifier_fdb_info *fdb_info;
1348+
struct dsa_slave_priv *p = netdev_priv(dev);
1349+
int err;
1350+
1351+
rtnl_lock();
1352+
switch (switchdev_work->event) {
1353+
case SWITCHDEV_FDB_ADD_TO_DEVICE:
1354+
fdb_info = &switchdev_work->fdb_info;
1355+
err = dsa_port_fdb_add(p->dp, fdb_info->addr, fdb_info->vid);
1356+
if (err) {
1357+
netdev_dbg(dev, "fdb add failed err=%d\n", err);
1358+
break;
1359+
}
1360+
call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev,
1361+
&fdb_info->info);
1362+
break;
1363+
1364+
case SWITCHDEV_FDB_DEL_TO_DEVICE:
1365+
fdb_info = &switchdev_work->fdb_info;
1366+
err = dsa_port_fdb_del(p->dp, fdb_info->addr, fdb_info->vid);
1367+
if (err) {
1368+
netdev_dbg(dev, "fdb del failed err=%d\n", err);
1369+
dev_close(dev);
1370+
}
1371+
break;
1372+
}
1373+
rtnl_unlock();
1374+
1375+
kfree(switchdev_work->fdb_info.addr);
1376+
kfree(switchdev_work);
1377+
dev_put(dev);
1378+
}
1379+
1380+
static int
1381+
dsa_slave_switchdev_fdb_work_init(struct dsa_switchdev_event_work *
1382+
switchdev_work,
1383+
const struct switchdev_notifier_fdb_info *
1384+
fdb_info)
1385+
{
1386+
memcpy(&switchdev_work->fdb_info, fdb_info,
1387+
sizeof(switchdev_work->fdb_info));
1388+
switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
1389+
if (!switchdev_work->fdb_info.addr)
1390+
return -ENOMEM;
1391+
ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
1392+
fdb_info->addr);
1393+
return 0;
1394+
}
1395+
1396+
/* Called under rcu_read_lock() */
1397+
static int dsa_slave_switchdev_event(struct notifier_block *unused,
1398+
unsigned long event, void *ptr)
1399+
{
1400+
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
1401+
struct dsa_switchdev_event_work *switchdev_work;
1402+
1403+
if (!dsa_slave_dev_check(dev))
1404+
return NOTIFY_DONE;
1405+
1406+
switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
1407+
if (!switchdev_work)
1408+
return NOTIFY_BAD;
1409+
1410+
INIT_WORK(&switchdev_work->work,
1411+
dsa_slave_switchdev_event_work);
1412+
switchdev_work->dev = dev;
1413+
switchdev_work->event = event;
1414+
1415+
switch (event) {
1416+
case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */
1417+
case SWITCHDEV_FDB_DEL_TO_DEVICE:
1418+
if (dsa_slave_switchdev_fdb_work_init(switchdev_work,
1419+
ptr))
1420+
goto err_fdb_work_init;
1421+
dev_hold(dev);
1422+
break;
1423+
default:
1424+
kfree(switchdev_work);
1425+
return NOTIFY_DONE;
1426+
}
1427+
1428+
dsa_schedule_work(&switchdev_work->work);
1429+
return NOTIFY_OK;
1430+
1431+
err_fdb_work_init:
1432+
kfree(switchdev_work);
1433+
return NOTIFY_BAD;
1434+
}
1435+
13351436
static struct notifier_block dsa_slave_nb __read_mostly = {
1336-
.notifier_call = dsa_slave_netdevice_event,
1437+
.notifier_call = dsa_slave_netdevice_event,
1438+
};
1439+
1440+
static struct notifier_block dsa_slave_switchdev_notifier = {
1441+
.notifier_call = dsa_slave_switchdev_event,
13371442
};
13381443

13391444
int dsa_slave_register_notifier(void)
13401445
{
1341-
return register_netdevice_notifier(&dsa_slave_nb);
1446+
int err;
1447+
1448+
err = register_netdevice_notifier(&dsa_slave_nb);
1449+
if (err)
1450+
return err;
1451+
1452+
err = register_switchdev_notifier(&dsa_slave_switchdev_notifier);
1453+
if (err)
1454+
goto err_switchdev_nb;
1455+
1456+
return 0;
1457+
1458+
err_switchdev_nb:
1459+
unregister_netdevice_notifier(&dsa_slave_nb);
1460+
return err;
13421461
}
13431462

13441463
void dsa_slave_unregister_notifier(void)
13451464
{
13461465
int err;
13471466

1467+
err = unregister_switchdev_notifier(&dsa_slave_switchdev_notifier);
1468+
if (err)
1469+
pr_err("DSA: failed to unregister switchdev notifier (%d)\n", err);
1470+
13481471
err = unregister_netdevice_notifier(&dsa_slave_nb);
13491472
if (err)
13501473
pr_err("DSA: failed to unregister slave notifier (%d)\n", err);

0 commit comments

Comments
 (0)