Skip to content

Commit

Permalink
mlxsw: spectrum_router: Direct macvlans' MACs to router
Browse files Browse the repository at this point in the history
An IP packet received on a netdev with a macvlan upper whose MAC matches
the packet's destination MAC will be re-injected to the Rx path as if it
was received by the macvlan, and perform an L3 lookup.

Reflect this functionality to the ASIC by programming FDB entries that
will direct MACs of macvlan uppers to the router.

In a similar fashion to router interfaces (RIFs) that are programmed
upon the addition of the first IP address on an interface and destroyed
upon the removal of the last IP address, the FDB entries for the macvlan
are added and destroyed based on the addition of the first and removal
of the last IP address on the macvlan.

Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Reviewed-by: Petr Machata <petrm@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Ido Schimmel authored and David S. Miller committed Jul 14, 2018
1 parent c551618 commit 2db9937
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 0 deletions.
11 changes: 11 additions & 0 deletions drivers/net/ethernet/mellanox/mlxsw/spectrum.c
Original file line number Diff line number Diff line change
Expand Up @@ -4490,6 +4490,9 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
err = mlxsw_sp_port_ovs_join(mlxsw_sp_port);
else
mlxsw_sp_port_ovs_leave(mlxsw_sp_port);
} else if (netif_is_macvlan(upper_dev)) {
if (!info->linking)
mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
}
break;
}
Expand Down Expand Up @@ -4606,6 +4609,9 @@ static int mlxsw_sp_netdevice_port_vlan_event(struct net_device *vlan_dev,
mlxsw_sp_port_bridge_leave(mlxsw_sp_port,
vlan_dev,
upper_dev);
} else if (netif_is_macvlan(upper_dev)) {
if (!info->linking)
mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
} else {
err = -EINVAL;
WARN_ON(1);
Expand Down Expand Up @@ -4684,6 +4690,11 @@ static int mlxsw_sp_netdevice_bridge_event(struct net_device *br_dev,
}
break;
case NETDEV_CHANGEUPPER:
upper_dev = info->upper_dev;
if (info->linking)
break;
if (netif_is_macvlan(upper_dev))
mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
break;
}

Expand Down
2 changes: 2 additions & 0 deletions drivers/net/ethernet/mellanox/mlxsw/spectrum.h
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,8 @@ static inline void mlxsw_sp_port_dcb_fini(struct mlxsw_sp_port *mlxsw_sp_port)
int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp);
void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp);
int mlxsw_sp_netdevice_router_port_event(struct net_device *dev);
void mlxsw_sp_rif_macvlan_del(struct mlxsw_sp *mlxsw_sp,
const struct net_device *macvlan_dev);
int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
unsigned long event, void *ptr);
int mlxsw_sp_inetaddr_valid_event(struct notifier_block *unused,
Expand Down
133 changes: 133 additions & 0 deletions drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include <linux/route.h>
#include <linux/gcd.h>
#include <linux/random.h>
#include <linux/if_macvlan.h>
#include <net/netevent.h>
#include <net/neighbour.h>
#include <net/arp.h>
Expand All @@ -60,6 +61,7 @@
#include <net/ndisc.h>
#include <net/ipv6.h>
#include <net/fib_notifier.h>
#include <net/switchdev.h>

#include "spectrum.h"
#include "core.h"
Expand Down Expand Up @@ -165,6 +167,7 @@ struct mlxsw_sp_rif_ops {
void (*deconfigure)(struct mlxsw_sp_rif *rif);
struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif,
struct netlink_ext_ack *extack);
void (*fdb_del)(struct mlxsw_sp_rif *rif, const char *mac);
};

static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree);
Expand Down Expand Up @@ -6027,6 +6030,12 @@ mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
!list_empty(&inet6_dev->addr_list))
addr_list_empty = false;

/* macvlans do not have a RIF, but rather piggy back on the
* RIF of their lower device.
*/
if (netif_is_macvlan(dev) && addr_list_empty)
return true;

if (rif && addr_list_empty &&
!netif_is_l3_slave(rif->dev))
return true;
Expand Down Expand Up @@ -6440,6 +6449,71 @@ static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
return 0;
}

static int mlxsw_sp_rif_macvlan_add(struct mlxsw_sp *mlxsw_sp,
const struct net_device *macvlan_dev,
struct netlink_ext_ack *extack)
{
struct macvlan_dev *vlan = netdev_priv(macvlan_dev);
struct mlxsw_sp_rif *rif;
int err;

rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, vlan->lowerdev);
if (!rif) {
NL_SET_ERR_MSG_MOD(extack, "macvlan is only supported on top of router interfaces");
return -EOPNOTSUPP;
}

err = mlxsw_sp_rif_fdb_op(mlxsw_sp, macvlan_dev->dev_addr,
mlxsw_sp_fid_index(rif->fid), true);
if (err)
return err;

/* Make sure the bridge driver does not have this MAC pointing at
* some other port.
*/
if (rif->ops->fdb_del)
rif->ops->fdb_del(rif, macvlan_dev->dev_addr);

return 0;
}

void mlxsw_sp_rif_macvlan_del(struct mlxsw_sp *mlxsw_sp,
const struct net_device *macvlan_dev)
{
struct macvlan_dev *vlan = netdev_priv(macvlan_dev);
struct mlxsw_sp_rif *rif;

rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, vlan->lowerdev);
/* If we do not have a RIF, then we already took care of
* removing the macvlan's MAC during RIF deletion.
*/
if (!rif)
return;
mlxsw_sp_rif_fdb_op(mlxsw_sp, macvlan_dev->dev_addr,
mlxsw_sp_fid_index(rif->fid), false);
}

static int mlxsw_sp_inetaddr_macvlan_event(struct net_device *macvlan_dev,
unsigned long event,
struct netlink_ext_ack *extack)
{
struct mlxsw_sp *mlxsw_sp;

mlxsw_sp = mlxsw_sp_lower_get(macvlan_dev);
if (!mlxsw_sp)
return 0;

switch (event) {
case NETDEV_UP:
return mlxsw_sp_rif_macvlan_add(mlxsw_sp, macvlan_dev, extack);
case NETDEV_DOWN:
mlxsw_sp_rif_macvlan_del(mlxsw_sp, macvlan_dev);
break;
}

return 0;
}

static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
unsigned long event,
struct netlink_ext_ack *extack)
Expand All @@ -6452,6 +6526,8 @@ static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
return mlxsw_sp_inetaddr_bridge_event(dev, event, extack);
else if (is_vlan_dev(dev))
return mlxsw_sp_inetaddr_vlan_event(dev, event, extack);
else if (netif_is_macvlan(dev))
return mlxsw_sp_inetaddr_macvlan_event(dev, event, extack);
else
return 0;
}
Expand Down Expand Up @@ -6716,6 +6792,27 @@ int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
return err;
}

static int __mlxsw_sp_rif_macvlan_flush(struct net_device *dev, void *data)
{
struct mlxsw_sp_rif *rif = data;

if (!netif_is_macvlan(dev))
return 0;

return mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, dev->dev_addr,
mlxsw_sp_fid_index(rif->fid), false);
}

static int mlxsw_sp_rif_macvlan_flush(struct mlxsw_sp_rif *rif)
{
if (!netif_is_macvlan_port(rif->dev))
return 0;

netdev_warn(rif->dev, "Router interface is deleted. Upper macvlans will not work\n");
return netdev_walk_all_upper_dev_rcu(rif->dev,
__mlxsw_sp_rif_macvlan_flush, rif);
}

static struct mlxsw_sp_rif_subport *
mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
{
Expand Down Expand Up @@ -6782,6 +6879,7 @@ static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
mlxsw_sp_fid_rif_set(fid, NULL);
mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
mlxsw_sp_fid_index(fid), false);
mlxsw_sp_rif_macvlan_flush(rif);
mlxsw_sp_rif_subport_op(rif, false);
}

Expand Down Expand Up @@ -6869,6 +6967,7 @@ static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
mlxsw_sp_fid_rif_set(fid, NULL);
mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
mlxsw_sp_fid_index(fid), false);
mlxsw_sp_rif_macvlan_flush(rif);
mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
mlxsw_sp_router_port(mlxsw_sp), false);
mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
Expand Down Expand Up @@ -6896,12 +6995,30 @@ mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif,
return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
}

static void mlxsw_sp_rif_vlan_fdb_del(struct mlxsw_sp_rif *rif, const char *mac)
{
u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
struct switchdev_notifier_fdb_info info;
struct net_device *br_dev;
struct net_device *dev;

br_dev = is_vlan_dev(rif->dev) ? vlan_dev_real_dev(rif->dev) : rif->dev;
dev = br_fdb_find_port(br_dev, mac, vid);
if (!dev)
return;

info.addr = mac;
info.vid = vid;
call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, dev, &info.info);
}

static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
.type = MLXSW_SP_RIF_TYPE_VLAN,
.rif_size = sizeof(struct mlxsw_sp_rif),
.configure = mlxsw_sp_rif_vlan_configure,
.deconfigure = mlxsw_sp_rif_vlan_deconfigure,
.fid_get = mlxsw_sp_rif_vlan_fid_get,
.fdb_del = mlxsw_sp_rif_vlan_fdb_del,
};

static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
Expand Down Expand Up @@ -6953,6 +7070,7 @@ static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
mlxsw_sp_fid_rif_set(fid, NULL);
mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
mlxsw_sp_fid_index(fid), false);
mlxsw_sp_rif_macvlan_flush(rif);
mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
mlxsw_sp_router_port(mlxsw_sp), false);
mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
Expand All @@ -6967,12 +7085,27 @@ mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif,
return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
}

static void mlxsw_sp_rif_fid_fdb_del(struct mlxsw_sp_rif *rif, const char *mac)
{
struct switchdev_notifier_fdb_info info;
struct net_device *dev;

dev = br_fdb_find_port(rif->dev, mac, 0);
if (!dev)
return;

info.addr = mac;
info.vid = 0;
call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, dev, &info.info);
}

static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
.type = MLXSW_SP_RIF_TYPE_FID,
.rif_size = sizeof(struct mlxsw_sp_rif),
.configure = mlxsw_sp_rif_fid_configure,
.deconfigure = mlxsw_sp_rif_fid_deconfigure,
.fid_get = mlxsw_sp_rif_fid_fid_get,
.fdb_del = mlxsw_sp_rif_fid_fdb_del,
};

static struct mlxsw_sp_rif_ipip_lb *
Expand Down

0 comments on commit 2db9937

Please sign in to comment.