Skip to content

Commit

Permalink
net: sparx5: Use Switchdev fdb events for managing fdb entries
Browse files Browse the repository at this point in the history
Changes the handling of fdb entries to use Switchdev events,
instead of the previous "sync_bridge" and "sync_port" which
only run when adding or removing VLANs on the bridge.

Signed-off-by: Casper Andersson <casper.casan@gmail.com>
Link: https://lore.kernel.org/r/20220314160918.4rfrrfgmbsf2pxl3@wse-c0155
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Casper Andersson authored and Jakub Kicinski committed Mar 16, 2022
1 parent 40867d7 commit 9f01cfb
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 72 deletions.
11 changes: 6 additions & 5 deletions drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,8 @@ static void sparx5_fdb_call_notifiers(enum switchdev_notifier_type type,
}

int sparx5_add_mact_entry(struct sparx5 *sparx5,
struct sparx5_port *port,
struct net_device *dev,
u16 portno,
const unsigned char *addr, u16 vid)
{
struct sparx5_mact_entry *mact_entry;
Expand All @@ -302,14 +303,14 @@ int sparx5_add_mact_entry(struct sparx5 *sparx5,
* mact thread to start the frame will reach CPU and the CPU will
* add the entry but without the extern_learn flag.
*/
mact_entry = find_mact_entry(sparx5, addr, vid, port->portno);
mact_entry = find_mact_entry(sparx5, addr, vid, portno);
if (mact_entry)
goto update_hw;

/* Add the entry in SW MAC table not to get the notification when
* SW is pulling again
*/
mact_entry = alloc_mact_entry(sparx5, addr, vid, port->portno);
mact_entry = alloc_mact_entry(sparx5, addr, vid, portno);
if (!mact_entry)
return -ENOMEM;

Expand All @@ -318,13 +319,13 @@ int sparx5_add_mact_entry(struct sparx5 *sparx5,
mutex_unlock(&sparx5->mact_lock);

update_hw:
ret = sparx5_mact_learn(sparx5, port->portno, addr, vid);
ret = sparx5_mact_learn(sparx5, portno, addr, vid);

/* New entry? */
if (mact_entry->flags == 0) {
mact_entry->flags |= MAC_ENT_LOCK; /* Don't age this */
sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, addr, vid,
port->ndev, true);
dev, true);
}

return ret;
Expand Down
3 changes: 2 additions & 1 deletion drivers/net/ethernet/microchip/sparx5/sparx5_main.h
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,8 @@ bool sparx5_mact_getnext(struct sparx5 *sparx5,
int sparx5_mact_forget(struct sparx5 *sparx5,
const unsigned char mac[ETH_ALEN], u16 vid);
int sparx5_add_mact_entry(struct sparx5 *sparx5,
struct sparx5_port *port,
struct net_device *dev,
u16 portno,
const unsigned char *addr, u16 vid);
int sparx5_del_mact_entry(struct sparx5 *sparx5,
const unsigned char *addr,
Expand Down
91 changes: 25 additions & 66 deletions drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct sparx5_switchdev_event_work {
struct work_struct work;
struct switchdev_notifier_fdb_info fdb_info;
struct net_device *dev;
struct sparx5 *sparx5;
unsigned long event;
};

Expand Down Expand Up @@ -247,31 +248,34 @@ static void sparx5_switchdev_bridge_fdb_event_work(struct work_struct *work)
struct switchdev_notifier_fdb_info *fdb_info;
struct sparx5_port *port;
struct sparx5 *sparx5;
bool host_addr;

rtnl_lock();
if (!sparx5_netdevice_check(dev))
goto out;

port = netdev_priv(dev);
sparx5 = port->sparx5;
if (!sparx5_netdevice_check(dev)) {
host_addr = true;
sparx5 = switchdev_work->sparx5;
} else {
host_addr = false;
sparx5 = switchdev_work->sparx5;
port = netdev_priv(dev);
}

fdb_info = &switchdev_work->fdb_info;

switch (switchdev_work->event) {
case SWITCHDEV_FDB_ADD_TO_DEVICE:
if (!fdb_info->added_by_user)
break;
sparx5_add_mact_entry(sparx5, port, fdb_info->addr,
fdb_info->vid);
if (host_addr)
sparx5_add_mact_entry(sparx5, dev, PGID_CPU,
fdb_info->addr, fdb_info->vid);
else
sparx5_add_mact_entry(sparx5, port->ndev, port->portno,
fdb_info->addr, fdb_info->vid);
break;
case SWITCHDEV_FDB_DEL_TO_DEVICE:
if (!fdb_info->added_by_user)
break;
sparx5_del_mact_entry(sparx5, fdb_info->addr, fdb_info->vid);
break;
}

out:
rtnl_unlock();
kfree(switchdev_work->fdb_info.addr);
kfree(switchdev_work);
Expand All @@ -283,15 +287,18 @@ static void sparx5_schedule_work(struct work_struct *work)
queue_work(sparx5_owq, work);
}

static int sparx5_switchdev_event(struct notifier_block *unused,
static int sparx5_switchdev_event(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
struct sparx5_switchdev_event_work *switchdev_work;
struct switchdev_notifier_fdb_info *fdb_info;
struct switchdev_notifier_info *info = ptr;
struct sparx5 *spx5;
int err;

spx5 = container_of(nb, struct sparx5, switchdev_nb);

switch (event) {
case SWITCHDEV_PORT_ATTR_SET:
err = switchdev_handle_port_attr_set(dev, ptr,
Expand All @@ -307,6 +314,7 @@ static int sparx5_switchdev_event(struct notifier_block *unused,

switchdev_work->dev = dev;
switchdev_work->event = event;
switchdev_work->sparx5 = spx5;

fdb_info = container_of(info,
struct switchdev_notifier_fdb_info,
Expand All @@ -333,54 +341,6 @@ static int sparx5_switchdev_event(struct notifier_block *unused,
return NOTIFY_BAD;
}

static void sparx5_sync_port_dev_addr(struct sparx5 *sparx5,
struct sparx5_port *port,
u16 vid, bool add)
{
if (!port ||
!test_bit(port->portno, sparx5->bridge_mask))
return; /* Skip null/host interfaces */

/* Bridge connects to vid? */
if (add) {
/* Add port MAC address from the VLAN */
sparx5_mact_learn(sparx5, PGID_CPU,
port->ndev->dev_addr, vid);
} else {
/* Control port addr visibility depending on
* port VLAN connectivity.
*/
if (test_bit(port->portno, sparx5->vlan_mask[vid]))
sparx5_mact_learn(sparx5, PGID_CPU,
port->ndev->dev_addr, vid);
else
sparx5_mact_forget(sparx5,
port->ndev->dev_addr, vid);
}
}

static void sparx5_sync_bridge_dev_addr(struct net_device *dev,
struct sparx5 *sparx5,
u16 vid, bool add)
{
int i;

/* First, handle bridge address'es */
if (add) {
sparx5_mact_learn(sparx5, PGID_CPU, dev->dev_addr,
vid);
sparx5_mact_learn(sparx5, PGID_BCAST, dev->broadcast,
vid);
} else {
sparx5_mact_forget(sparx5, dev->dev_addr, vid);
sparx5_mact_forget(sparx5, dev->broadcast, vid);
}

/* Now look at bridged ports */
for (i = 0; i < SPX5_PORTS; i++)
sparx5_sync_port_dev_addr(sparx5, sparx5->ports[i], vid, add);
}

static int sparx5_handle_port_vlan_add(struct net_device *dev,
struct notifier_block *nb,
const struct switchdev_obj_port_vlan *v)
Expand All @@ -392,7 +352,9 @@ static int sparx5_handle_port_vlan_add(struct net_device *dev,
container_of(nb, struct sparx5,
switchdev_blocking_nb);

sparx5_sync_bridge_dev_addr(dev, sparx5, v->vid, true);
/* Flood broadcast to CPU */
sparx5_mact_learn(sparx5, PGID_BCAST, dev->broadcast,
v->vid);
return 0;
}

Expand Down Expand Up @@ -438,7 +400,7 @@ static int sparx5_handle_port_vlan_del(struct net_device *dev,
container_of(nb, struct sparx5,
switchdev_blocking_nb);

sparx5_sync_bridge_dev_addr(dev, sparx5, vid, false);
sparx5_mact_forget(sparx5, dev->broadcast, vid);
return 0;
}

Expand All @@ -449,9 +411,6 @@ static int sparx5_handle_port_vlan_del(struct net_device *dev,
if (ret)
return ret;

/* Delete the port MAC address with the matching VLAN information */
sparx5_mact_forget(port->sparx5, port->ndev->dev_addr, vid);

return 0;
}

Expand Down

0 comments on commit 9f01cfb

Please sign in to comment.