Commit 9f4c2cff authored by David S. Miller's avatar David S. Miller

Merge branch 'mlxsw-Un-offload-FDB-on-NVE-detach-attach'

Ido Schimmel says:

====================
mlxsw: Un/offload FDB on NVE detach/attach

Petr says:

When a VXLAN device is attached to a bridge of a driver capable of
offloading such, or upped, the FDB entries already present at the device
need to be offloaded. Similarly when an offloaded VXLAN device ceases
being interesting (it is downed, or detached, or a front-panel port
netdevice is detached from the bridge that the VXLAN device is attached
to), any offloaded FDB entries need to be unoffloaded and unmarked. This
attach / detach processing is implemented in this patchset.

In patch #1, a code pattern is extracted into a named function for
easier reuse.

In patch #2, vxlan_fdb_replay() is added to send
SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE for each FDB entry with a given VNI.
The intention is that the offloading driver will interpret these events
like any other and thus offload the FDB entries that existed prior to
VXLAN attach.

In patches #3 and #4, the functions vxlan_fdb_clear_offload() resp.
br_fdb_clear_offload() are added. These clear the offloaded flag at
matching FDB entries.

In patches #5-#9, we introduce FID-type-specific and NVE-type-specific
ops necessary to properly abstract invocations of the replay/clear
functions.

Finally patch #10 implements the FDB management.

In patch #11, the mlxsw-specific test case is extended to check that the
management of offload marks under the newly-supported situations is
correct. Patch #12, from Ido, exercises the new code paths in actual
functional test.

v2:
- Patch #1:
    - Modify vxlan_fdb_switchdev_notifier_info() to initialize the
      structure through a passed-in pointer argument, instead of returning
      it as a value.
- Patch #2:
    - Adapt to API change in vxlan_fdb_switchdev_notifier_info()
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 6b241e41 55939b26
...@@ -81,6 +81,10 @@ enum mlxsw_sp_fid_type { ...@@ -81,6 +81,10 @@ enum mlxsw_sp_fid_type {
MLXSW_SP_FID_TYPE_MAX, MLXSW_SP_FID_TYPE_MAX,
}; };
enum mlxsw_sp_nve_type {
MLXSW_SP_NVE_TYPE_VXLAN,
};
struct mlxsw_sp_mid { struct mlxsw_sp_mid {
struct list_head list; struct list_head list;
unsigned char addr[ETH_ALEN]; unsigned char addr[ETH_ALEN];
...@@ -383,6 +387,7 @@ int mlxsw_sp_bridge_vxlan_join(struct mlxsw_sp *mlxsw_sp, ...@@ -383,6 +387,7 @@ int mlxsw_sp_bridge_vxlan_join(struct mlxsw_sp *mlxsw_sp,
struct netlink_ext_ack *extack); struct netlink_ext_ack *extack);
void mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp, void mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp,
const struct net_device *vxlan_dev); const struct net_device *vxlan_dev);
extern struct notifier_block mlxsw_sp_switchdev_notifier;
/* spectrum.c */ /* spectrum.c */
int mlxsw_sp_port_ets_set(struct mlxsw_sp_port *mlxsw_sp_port, int mlxsw_sp_port_ets_set(struct mlxsw_sp_port *mlxsw_sp_port,
...@@ -745,6 +750,8 @@ bool mlxsw_sp_fid_lag_vid_valid(const struct mlxsw_sp_fid *fid); ...@@ -745,6 +750,8 @@ bool mlxsw_sp_fid_lag_vid_valid(const struct mlxsw_sp_fid *fid);
struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_index(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_index(struct mlxsw_sp *mlxsw_sp,
u16 fid_index); u16 fid_index);
int mlxsw_sp_fid_nve_ifindex(const struct mlxsw_sp_fid *fid, int *nve_ifindex); int mlxsw_sp_fid_nve_ifindex(const struct mlxsw_sp_fid *fid, int *nve_ifindex);
int mlxsw_sp_fid_nve_type(const struct mlxsw_sp_fid *fid,
enum mlxsw_sp_nve_type *p_type);
struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_vni(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_vni(struct mlxsw_sp *mlxsw_sp,
__be32 vni); __be32 vni);
int mlxsw_sp_fid_vni(const struct mlxsw_sp_fid *fid, __be32 *vni); int mlxsw_sp_fid_vni(const struct mlxsw_sp_fid *fid, __be32 *vni);
...@@ -752,9 +759,12 @@ int mlxsw_sp_fid_nve_flood_index_set(struct mlxsw_sp_fid *fid, ...@@ -752,9 +759,12 @@ int mlxsw_sp_fid_nve_flood_index_set(struct mlxsw_sp_fid *fid,
u32 nve_flood_index); u32 nve_flood_index);
void mlxsw_sp_fid_nve_flood_index_clear(struct mlxsw_sp_fid *fid); void mlxsw_sp_fid_nve_flood_index_clear(struct mlxsw_sp_fid *fid);
bool mlxsw_sp_fid_nve_flood_index_is_set(const struct mlxsw_sp_fid *fid); bool mlxsw_sp_fid_nve_flood_index_is_set(const struct mlxsw_sp_fid *fid);
int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, __be32 vni, int nve_ifindex); int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, enum mlxsw_sp_nve_type type,
__be32 vni, int nve_ifindex);
void mlxsw_sp_fid_vni_clear(struct mlxsw_sp_fid *fid); void mlxsw_sp_fid_vni_clear(struct mlxsw_sp_fid *fid);
bool mlxsw_sp_fid_vni_is_set(const struct mlxsw_sp_fid *fid); bool mlxsw_sp_fid_vni_is_set(const struct mlxsw_sp_fid *fid);
void mlxsw_sp_fid_fdb_clear_offload(const struct mlxsw_sp_fid *fid,
const struct net_device *nve_dev);
int mlxsw_sp_fid_flood_set(struct mlxsw_sp_fid *fid, int mlxsw_sp_fid_flood_set(struct mlxsw_sp_fid *fid,
enum mlxsw_sp_flood_type packet_type, u8 local_port, enum mlxsw_sp_flood_type packet_type, u8 local_port,
bool member); bool member);
...@@ -823,10 +833,6 @@ extern const struct mlxsw_sp_mr_tcam_ops mlxsw_sp1_mr_tcam_ops; ...@@ -823,10 +833,6 @@ extern const struct mlxsw_sp_mr_tcam_ops mlxsw_sp1_mr_tcam_ops;
extern const struct mlxsw_sp_mr_tcam_ops mlxsw_sp2_mr_tcam_ops; extern const struct mlxsw_sp_mr_tcam_ops mlxsw_sp2_mr_tcam_ops;
/* spectrum_nve.c */ /* spectrum_nve.c */
enum mlxsw_sp_nve_type {
MLXSW_SP_NVE_TYPE_VXLAN,
};
struct mlxsw_sp_nve_params { struct mlxsw_sp_nve_params {
enum mlxsw_sp_nve_type type; enum mlxsw_sp_nve_type type;
__be32 vni; __be32 vni;
......
...@@ -30,6 +30,7 @@ struct mlxsw_sp_fid { ...@@ -30,6 +30,7 @@ struct mlxsw_sp_fid {
struct rhash_head ht_node; struct rhash_head ht_node;
struct rhash_head vni_ht_node; struct rhash_head vni_ht_node;
enum mlxsw_sp_nve_type nve_type;
__be32 vni; __be32 vni;
u32 nve_flood_index; u32 nve_flood_index;
int nve_ifindex; int nve_ifindex;
...@@ -84,6 +85,8 @@ struct mlxsw_sp_fid_ops { ...@@ -84,6 +85,8 @@ struct mlxsw_sp_fid_ops {
int (*nve_flood_index_set)(struct mlxsw_sp_fid *fid, int (*nve_flood_index_set)(struct mlxsw_sp_fid *fid,
u32 nve_flood_index); u32 nve_flood_index);
void (*nve_flood_index_clear)(struct mlxsw_sp_fid *fid); void (*nve_flood_index_clear)(struct mlxsw_sp_fid *fid);
void (*fdb_clear_offload)(const struct mlxsw_sp_fid *fid,
const struct net_device *nve_dev);
}; };
struct mlxsw_sp_fid_family { struct mlxsw_sp_fid_family {
...@@ -151,6 +154,17 @@ int mlxsw_sp_fid_nve_ifindex(const struct mlxsw_sp_fid *fid, int *nve_ifindex) ...@@ -151,6 +154,17 @@ int mlxsw_sp_fid_nve_ifindex(const struct mlxsw_sp_fid *fid, int *nve_ifindex)
return 0; return 0;
} }
int mlxsw_sp_fid_nve_type(const struct mlxsw_sp_fid *fid,
enum mlxsw_sp_nve_type *p_type)
{
if (!fid->vni_valid)
return -EINVAL;
*p_type = fid->nve_type;
return 0;
}
struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_vni(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_vni(struct mlxsw_sp *mlxsw_sp,
__be32 vni) __be32 vni)
{ {
...@@ -211,7 +225,8 @@ bool mlxsw_sp_fid_nve_flood_index_is_set(const struct mlxsw_sp_fid *fid) ...@@ -211,7 +225,8 @@ bool mlxsw_sp_fid_nve_flood_index_is_set(const struct mlxsw_sp_fid *fid)
return fid->nve_flood_index_valid; return fid->nve_flood_index_valid;
} }
int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, __be32 vni, int nve_ifindex) int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, enum mlxsw_sp_nve_type type,
__be32 vni, int nve_ifindex)
{ {
struct mlxsw_sp_fid_family *fid_family = fid->fid_family; struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
const struct mlxsw_sp_fid_ops *ops = fid_family->ops; const struct mlxsw_sp_fid_ops *ops = fid_family->ops;
...@@ -221,6 +236,7 @@ int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, __be32 vni, int nve_ifindex) ...@@ -221,6 +236,7 @@ int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, __be32 vni, int nve_ifindex)
if (WARN_ON(!ops->vni_set || fid->vni_valid)) if (WARN_ON(!ops->vni_set || fid->vni_valid))
return -EINVAL; return -EINVAL;
fid->nve_type = type;
fid->nve_ifindex = nve_ifindex; fid->nve_ifindex = nve_ifindex;
fid->vni = vni; fid->vni = vni;
err = rhashtable_lookup_insert_fast(&mlxsw_sp->fid_core->vni_ht, err = rhashtable_lookup_insert_fast(&mlxsw_sp->fid_core->vni_ht,
...@@ -263,6 +279,16 @@ bool mlxsw_sp_fid_vni_is_set(const struct mlxsw_sp_fid *fid) ...@@ -263,6 +279,16 @@ bool mlxsw_sp_fid_vni_is_set(const struct mlxsw_sp_fid *fid)
return fid->vni_valid; return fid->vni_valid;
} }
void mlxsw_sp_fid_fdb_clear_offload(const struct mlxsw_sp_fid *fid,
const struct net_device *nve_dev)
{
struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
const struct mlxsw_sp_fid_ops *ops = fid_family->ops;
if (ops->fdb_clear_offload)
ops->fdb_clear_offload(fid, nve_dev);
}
static const struct mlxsw_sp_flood_table * static const struct mlxsw_sp_flood_table *
mlxsw_sp_fid_flood_table_lookup(const struct mlxsw_sp_fid *fid, mlxsw_sp_fid_flood_table_lookup(const struct mlxsw_sp_fid *fid,
enum mlxsw_sp_flood_type packet_type) enum mlxsw_sp_flood_type packet_type)
...@@ -752,6 +778,13 @@ static void mlxsw_sp_fid_8021d_nve_flood_index_clear(struct mlxsw_sp_fid *fid) ...@@ -752,6 +778,13 @@ static void mlxsw_sp_fid_8021d_nve_flood_index_clear(struct mlxsw_sp_fid *fid)
fid->vni_valid, 0, false); fid->vni_valid, 0, false);
} }
static void
mlxsw_sp_fid_8021d_fdb_clear_offload(const struct mlxsw_sp_fid *fid,
const struct net_device *nve_dev)
{
br_fdb_clear_offload(nve_dev, 0);
}
static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021d_ops = { static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021d_ops = {
.setup = mlxsw_sp_fid_8021d_setup, .setup = mlxsw_sp_fid_8021d_setup,
.configure = mlxsw_sp_fid_8021d_configure, .configure = mlxsw_sp_fid_8021d_configure,
...@@ -765,6 +798,7 @@ static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021d_ops = { ...@@ -765,6 +798,7 @@ static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021d_ops = {
.vni_clear = mlxsw_sp_fid_8021d_vni_clear, .vni_clear = mlxsw_sp_fid_8021d_vni_clear,
.nve_flood_index_set = mlxsw_sp_fid_8021d_nve_flood_index_set, .nve_flood_index_set = mlxsw_sp_fid_8021d_nve_flood_index_set,
.nve_flood_index_clear = mlxsw_sp_fid_8021d_nve_flood_index_clear, .nve_flood_index_clear = mlxsw_sp_fid_8021d_nve_flood_index_clear,
.fdb_clear_offload = mlxsw_sp_fid_8021d_fdb_clear_offload,
}; };
static const struct mlxsw_sp_flood_table mlxsw_sp_fid_8021d_flood_tables[] = { static const struct mlxsw_sp_flood_table mlxsw_sp_fid_8021d_flood_tables[] = {
...@@ -801,6 +835,13 @@ static const struct mlxsw_sp_fid_family mlxsw_sp_fid_8021d_family = { ...@@ -801,6 +835,13 @@ static const struct mlxsw_sp_fid_family mlxsw_sp_fid_8021d_family = {
.lag_vid_valid = 1, .lag_vid_valid = 1,
}; };
static void
mlxsw_sp_fid_8021q_fdb_clear_offload(const struct mlxsw_sp_fid *fid,
const struct net_device *nve_dev)
{
br_fdb_clear_offload(nve_dev, mlxsw_sp_fid_8021q_vid(fid));
}
static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021q_emu_ops = { static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021q_emu_ops = {
.setup = mlxsw_sp_fid_8021q_setup, .setup = mlxsw_sp_fid_8021q_setup,
.configure = mlxsw_sp_fid_8021d_configure, .configure = mlxsw_sp_fid_8021d_configure,
...@@ -814,6 +855,7 @@ static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021q_emu_ops = { ...@@ -814,6 +855,7 @@ static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021q_emu_ops = {
.vni_clear = mlxsw_sp_fid_8021d_vni_clear, .vni_clear = mlxsw_sp_fid_8021d_vni_clear,
.nve_flood_index_set = mlxsw_sp_fid_8021d_nve_flood_index_set, .nve_flood_index_set = mlxsw_sp_fid_8021d_nve_flood_index_set,
.nve_flood_index_clear = mlxsw_sp_fid_8021d_nve_flood_index_clear, .nve_flood_index_clear = mlxsw_sp_fid_8021d_nve_flood_index_clear,
.fdb_clear_offload = mlxsw_sp_fid_8021q_fdb_clear_offload,
}; };
/* There are 4K-2 emulated 802.1Q FIDs, starting right after the 802.1D FIDs */ /* There are 4K-2 emulated 802.1Q FIDs, starting right after the 802.1D FIDs */
......
...@@ -789,6 +789,21 @@ static void mlxsw_sp_nve_fdb_flush_by_fid(struct mlxsw_sp *mlxsw_sp, ...@@ -789,6 +789,21 @@ static void mlxsw_sp_nve_fdb_flush_by_fid(struct mlxsw_sp *mlxsw_sp,
mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdf), sfdf_pl); mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdf), sfdf_pl);
} }
static void mlxsw_sp_nve_fdb_clear_offload(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_fid *fid,
const struct net_device *nve_dev,
__be32 vni)
{
const struct mlxsw_sp_nve_ops *ops;
enum mlxsw_sp_nve_type type;
if (WARN_ON(mlxsw_sp_fid_nve_type(fid, &type)))
return;
ops = mlxsw_sp->nve->nve_ops_arr[type];
ops->fdb_clear_offload(nve_dev, vni);
}
int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid, int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
struct mlxsw_sp_nve_params *params, struct mlxsw_sp_nve_params *params,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
...@@ -817,7 +832,8 @@ int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid, ...@@ -817,7 +832,8 @@ int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
return err; return err;
} }
err = mlxsw_sp_fid_vni_set(fid, params->vni, params->dev->ifindex); err = mlxsw_sp_fid_vni_set(fid, params->type, params->vni,
params->dev->ifindex);
if (err) { if (err) {
NL_SET_ERR_MSG_MOD(extack, "Failed to set VNI on FID"); NL_SET_ERR_MSG_MOD(extack, "Failed to set VNI on FID");
goto err_fid_vni_set; goto err_fid_vni_set;
...@@ -825,8 +841,16 @@ int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid, ...@@ -825,8 +841,16 @@ int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
nve->config = config; nve->config = config;
err = ops->fdb_replay(params->dev, params->vni);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Failed to offload the FDB");
goto err_fdb_replay;
}
return 0; return 0;
err_fdb_replay:
mlxsw_sp_fid_vni_clear(fid);
err_fid_vni_set: err_fid_vni_set:
mlxsw_sp_nve_tunnel_fini(mlxsw_sp); mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
return err; return err;
...@@ -836,9 +860,27 @@ void mlxsw_sp_nve_fid_disable(struct mlxsw_sp *mlxsw_sp, ...@@ -836,9 +860,27 @@ void mlxsw_sp_nve_fid_disable(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fid *fid) struct mlxsw_sp_fid *fid)
{ {
u16 fid_index = mlxsw_sp_fid_index(fid); u16 fid_index = mlxsw_sp_fid_index(fid);
struct net_device *nve_dev;
int nve_ifindex;
__be32 vni;
mlxsw_sp_nve_flood_ip_flush(mlxsw_sp, fid); mlxsw_sp_nve_flood_ip_flush(mlxsw_sp, fid);
mlxsw_sp_nve_fdb_flush_by_fid(mlxsw_sp, fid_index); mlxsw_sp_nve_fdb_flush_by_fid(mlxsw_sp, fid_index);
if (WARN_ON(mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex) ||
mlxsw_sp_fid_vni(fid, &vni)))
goto out;
nve_dev = dev_get_by_index(&init_net, nve_ifindex);
if (!nve_dev)
goto out;
mlxsw_sp_nve_fdb_clear_offload(mlxsw_sp, fid, nve_dev, vni);
mlxsw_sp_fid_fdb_clear_offload(fid, nve_dev);
dev_put(nve_dev);
out:
mlxsw_sp_fid_vni_clear(fid); mlxsw_sp_fid_vni_clear(fid);
mlxsw_sp_nve_tunnel_fini(mlxsw_sp); mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
} }
......
...@@ -41,6 +41,8 @@ struct mlxsw_sp_nve_ops { ...@@ -41,6 +41,8 @@ struct mlxsw_sp_nve_ops {
int (*init)(struct mlxsw_sp_nve *nve, int (*init)(struct mlxsw_sp_nve *nve,
const struct mlxsw_sp_nve_config *config); const struct mlxsw_sp_nve_config *config);
void (*fini)(struct mlxsw_sp_nve *nve); void (*fini)(struct mlxsw_sp_nve *nve);
int (*fdb_replay)(const struct net_device *nve_dev, __be32 vni);
void (*fdb_clear_offload)(const struct net_device *nve_dev, __be32 vni);
}; };
extern const struct mlxsw_sp_nve_ops mlxsw_sp1_nve_vxlan_ops; extern const struct mlxsw_sp_nve_ops mlxsw_sp1_nve_vxlan_ops;
......
...@@ -211,12 +211,30 @@ static void mlxsw_sp1_nve_vxlan_fini(struct mlxsw_sp_nve *nve) ...@@ -211,12 +211,30 @@ static void mlxsw_sp1_nve_vxlan_fini(struct mlxsw_sp_nve *nve)
config->udp_dport); config->udp_dport);
} }
static int
mlxsw_sp_nve_vxlan_fdb_replay(const struct net_device *nve_dev, __be32 vni)
{
if (WARN_ON(!netif_is_vxlan(nve_dev)))
return -EINVAL;
return vxlan_fdb_replay(nve_dev, vni, &mlxsw_sp_switchdev_notifier);
}
static void
mlxsw_sp_nve_vxlan_clear_offload(const struct net_device *nve_dev, __be32 vni)
{
if (WARN_ON(!netif_is_vxlan(nve_dev)))
return;
vxlan_fdb_clear_offload(nve_dev, vni);
}
const struct mlxsw_sp_nve_ops mlxsw_sp1_nve_vxlan_ops = { const struct mlxsw_sp_nve_ops mlxsw_sp1_nve_vxlan_ops = {
.type = MLXSW_SP_NVE_TYPE_VXLAN, .type = MLXSW_SP_NVE_TYPE_VXLAN,
.can_offload = mlxsw_sp1_nve_vxlan_can_offload, .can_offload = mlxsw_sp1_nve_vxlan_can_offload,
.nve_config = mlxsw_sp_nve_vxlan_config, .nve_config = mlxsw_sp_nve_vxlan_config,
.init = mlxsw_sp1_nve_vxlan_init, .init = mlxsw_sp1_nve_vxlan_init,
.fini = mlxsw_sp1_nve_vxlan_fini, .fini = mlxsw_sp1_nve_vxlan_fini,
.fdb_replay = mlxsw_sp_nve_vxlan_fdb_replay,
.fdb_clear_offload = mlxsw_sp_nve_vxlan_clear_offload,
}; };
static bool mlxsw_sp2_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve, static bool mlxsw_sp2_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve,
...@@ -242,4 +260,6 @@ const struct mlxsw_sp_nve_ops mlxsw_sp2_nve_vxlan_ops = { ...@@ -242,4 +260,6 @@ const struct mlxsw_sp_nve_ops mlxsw_sp2_nve_vxlan_ops = {
.nve_config = mlxsw_sp_nve_vxlan_config, .nve_config = mlxsw_sp_nve_vxlan_config,
.init = mlxsw_sp2_nve_vxlan_init, .init = mlxsw_sp2_nve_vxlan_init,
.fini = mlxsw_sp2_nve_vxlan_fini, .fini = mlxsw_sp2_nve_vxlan_fini,
.fdb_replay = mlxsw_sp_nve_vxlan_fdb_replay,
.fdb_clear_offload = mlxsw_sp_nve_vxlan_clear_offload,
}; };
...@@ -3186,7 +3186,7 @@ static int mlxsw_sp_switchdev_event(struct notifier_block *unused, ...@@ -3186,7 +3186,7 @@ static int mlxsw_sp_switchdev_event(struct notifier_block *unused,
return NOTIFY_BAD; return NOTIFY_BAD;
} }
static struct notifier_block mlxsw_sp_switchdev_notifier = { struct notifier_block mlxsw_sp_switchdev_notifier = {
.notifier_call = mlxsw_sp_switchdev_event, .notifier_call = mlxsw_sp_switchdev_event,
}; };
......
...@@ -358,6 +358,22 @@ static void __vxlan_fdb_notify(struct vxlan_dev *vxlan, struct vxlan_fdb *fdb, ...@@ -358,6 +358,22 @@ static void __vxlan_fdb_notify(struct vxlan_dev *vxlan, struct vxlan_fdb *fdb,
rtnl_set_sk_err(net, RTNLGRP_NEIGH, err); rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
} }
static void vxlan_fdb_switchdev_notifier_info(const struct vxlan_dev *vxlan,
const struct vxlan_fdb *fdb,
const struct vxlan_rdst *rd,
struct switchdev_notifier_vxlan_fdb_info *fdb_info)
{
fdb_info->info.dev = vxlan->dev;
fdb_info->remote_ip = rd->remote_ip;
fdb_info->remote_port = rd->remote_port;
fdb_info->remote_vni = rd->remote_vni;
fdb_info->remote_ifindex = rd->remote_ifindex;
memcpy(fdb_info->eth_addr, fdb->eth_addr, ETH_ALEN);
fdb_info->vni = fdb->vni;
fdb_info->offloaded = rd->offloaded;
fdb_info->added_by_user = fdb->flags & NTF_VXLAN_ADDED_BY_USER;
}
static void vxlan_fdb_switchdev_call_notifiers(struct vxlan_dev *vxlan, static void vxlan_fdb_switchdev_call_notifiers(struct vxlan_dev *vxlan,
struct vxlan_fdb *fdb, struct vxlan_fdb *fdb,
struct vxlan_rdst *rd, struct vxlan_rdst *rd,
...@@ -371,18 +387,7 @@ static void vxlan_fdb_switchdev_call_notifiers(struct vxlan_dev *vxlan, ...@@ -371,18 +387,7 @@ static void vxlan_fdb_switchdev_call_notifiers(struct vxlan_dev *vxlan,
notifier_type = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE notifier_type = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE
: SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE; : SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE;
vxlan_fdb_switchdev_notifier_info(vxlan, fdb, rd, &info);
info = (struct switchdev_notifier_vxlan_fdb_info){
.remote_ip = rd->remote_ip,
.remote_port = rd->remote_port,
.remote_vni = rd->remote_vni,
.remote_ifindex = rd->remote_ifindex,
.vni = fdb->vni,
.offloaded = rd->offloaded,
.added_by_user = fdb->flags & NTF_VXLAN_ADDED_BY_USER,
};
memcpy(info.eth_addr, fdb->eth_addr, ETH_ALEN);
call_switchdev_notifiers(notifier_type, vxlan->dev, call_switchdev_notifiers(notifier_type, vxlan->dev,
&info.info); &info.info);
} }
...@@ -539,17 +544,7 @@ int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni, ...@@ -539,17 +544,7 @@ int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
} }
rdst = first_remote_rcu(f); rdst = first_remote_rcu(f);
vxlan_fdb_switchdev_notifier_info(vxlan, f, rdst, fdb_info);
memset(fdb_info, 0, sizeof(*fdb_info));
fdb_info->info.dev = dev;
fdb_info->remote_ip = rdst->remote_ip;
fdb_info->remote_port = rdst->remote_port;
fdb_info->remote_vni = rdst->remote_vni;
fdb_info->remote_ifindex = rdst->remote_ifindex;
fdb_info->vni = vni;
fdb_info->offloaded = rdst->offloaded;
fdb_info->added_by_user = f->flags & NTF_VXLAN_ADDED_BY_USER;
ether_addr_copy(fdb_info->eth_addr, mac);
out: out:
rcu_read_unlock(); rcu_read_unlock();
...@@ -557,6 +552,75 @@ int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni, ...@@ -557,6 +552,75 @@ int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
} }
EXPORT_SYMBOL_GPL(vxlan_fdb_find_uc); EXPORT_SYMBOL_GPL(vxlan_fdb_find_uc);
static int vxlan_fdb_notify_one(struct notifier_block *nb,
const struct vxlan_dev *vxlan,
const struct vxlan_fdb *f,
const struct vxlan_rdst *rdst)
{
struct switchdev_notifier_vxlan_fdb_info fdb_info;
int rc;
vxlan_fdb_switchdev_notifier_info(vxlan, f, rdst, &fdb_info);
rc = nb->notifier_call(nb, SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE,
&fdb_info);
return notifier_to_errno(rc);
}
int vxlan_fdb_replay(const struct net_device *dev, __be32 vni,
struct notifier_block *nb)
{
struct vxlan_dev *vxlan;
struct vxlan_rdst *rdst;
struct vxlan_fdb *f;
unsigned int h;
int rc = 0;
if (!netif_is_vxlan(dev))
return -EINVAL;
vxlan = netdev_priv(dev);
spin_lock_bh(&vxlan->hash_lock);
for (h = 0; h < FDB_HASH_SIZE; ++h) {
hlist_for_each_entry(f, &vxlan->fdb_head[h], hlist) {
if (f->vni == vni) {
list_for_each_entry(rdst, &f->remotes, list) {
rc = vxlan_fdb_notify_one(nb, vxlan,
f, rdst);
if (rc)
goto out;
}
}
}
}
out:
spin_unlock_bh(&vxlan->hash_lock);
return rc;
}
EXPORT_SYMBOL_GPL(vxlan_fdb_replay);
void vxlan_fdb_clear_offload(const struct net_device *dev, __be32 vni)
{
struct vxlan_dev *vxlan;
struct vxlan_rdst *rdst;
struct vxlan_fdb *f;
unsigned int h;
if (!netif_is_vxlan(dev))
return;
vxlan = netdev_priv(dev);
spin_lock_bh(&vxlan->hash_lock);
for (h = 0; h < FDB_HASH_SIZE; ++h) {
hlist_for_each_entry(f, &vxlan->fdb_head[h], hlist)
if (f->vni == vni)
list_for_each_entry(rdst, &f->remotes, list)
rdst->offloaded = false;
}
spin_unlock_bh(&vxlan->hash_lock);
}
EXPORT_SYMBOL_GPL(vxlan_fdb_clear_offload);
/* Replace destination of unicast mac */ /* Replace destination of unicast mac */
static int vxlan_fdb_replace(struct vxlan_fdb *f, static int vxlan_fdb_replace(struct vxlan_fdb *f,
union vxlan_addr *ip, __be16 port, __be32 vni, union vxlan_addr *ip, __be16 port, __be32 vni,
......
...@@ -119,6 +119,7 @@ static inline int br_vlan_get_info(const struct net_device *dev, u16 vid, ...@@ -119,6 +119,7 @@ static inline int br_vlan_get_info(const struct net_device *dev, u16 vid,
struct net_device *br_fdb_find_port(const struct net_device *br_dev, struct net_device *br_fdb_find_port(const struct net_device *br_dev,
const unsigned char *addr, const unsigned char *addr,
__u16 vid); __u16 vid);
void br_fdb_clear_offload(const struct net_device *dev, u16 vid);
bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag); bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag);
#else #else
static inline struct net_device * static inline struct net_device *
...@@ -128,6 +129,11 @@ br_fdb_find_port(const struct net_device *br_dev, ...@@ -128,6 +129,11 @@ br_fdb_find_port(const struct net_device *br_dev,
{ {
return NULL; return NULL;
} }
static inline void br_fdb_clear_offload(const struct net_device *dev, u16 vid)
{
}
static inline bool static inline bool
br_port_flag_is_set(const struct net_device *dev, unsigned long flag) br_port_flag_is_set(const struct net_device *dev, unsigned long flag)
{ {
......
...@@ -427,6 +427,10 @@ struct switchdev_notifier_vxlan_fdb_info { ...@@ -427,6 +427,10 @@ struct switchdev_notifier_vxlan_fdb_info {
#if IS_ENABLED(CONFIG_VXLAN) #if IS_ENABLED(CONFIG_VXLAN)
int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni, int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
struct switchdev_notifier_vxlan_fdb_info *fdb_info); struct switchdev_notifier_vxlan_fdb_info *fdb_info);
int vxlan_fdb_replay(const struct net_device *dev, __be32 vni,
struct notifier_block *nb);
void vxlan_fdb_clear_offload(const struct net_device *dev, __be32 vni);
#else #else
static inline int static inline int
vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni, vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
...@@ -434,6 +438,17 @@ vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni, ...@@ -434,6 +438,17 @@ vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
{ {
return -ENOENT; return -ENOENT;
} }
static inline int vxlan_fdb_replay(const struct net_device *dev, __be32 vni,
struct notifier_block *nb)
{
return -EOPNOTSUPP;
}
static inline void
vxlan_fdb_clear_offload(const struct net_device *dev, __be32 vni)
{
}
#endif #endif
#endif #endif
...@@ -1164,3 +1164,23 @@ void br_fdb_offloaded_set(struct net_bridge *br, struct net_bridge_port *p, ...@@ -1164,3 +1164,23 @@ void br_fdb_offloaded_set(struct net_bridge *br, struct net_bridge_port *p,
spin_unlock_bh(&br->hash_lock); spin_unlock_bh(&br->hash_lock);
} }
void br_fdb_clear_offload(const struct net_device *dev, u16 vid)
{
struct net_bridge_fdb_entry *f;
struct net_bridge_port *p;
ASSERT_RTNL();
p = br_port_get_rtnl(dev);
if (!p)
return;
spin_lock_bh(&p->br->hash_lock);
hlist_for_each_entry(f, &p->br->fdb_list, fdb_node) {
if (f->dst == p && f->key.vlan_id == vid)
f->offloaded = 0;
}
spin_unlock_bh(&p->br->hash_lock);
}
EXPORT_SYMBOL_GPL(br_fdb_clear_offload);
...@@ -647,12 +647,159 @@ offload_indication_decap_route_test() ...@@ -647,12 +647,159 @@ offload_indication_decap_route_test()
noudpcsum ttl 20 tos inherit local 198.51.100.1 dstport 4789 noudpcsum ttl 20 tos inherit local 198.51.100.1 dstport 4789
} }
check_fdb_offloaded()
{
local mac=00:11:22:33:44:55
local zmac=00:00:00:00:00:00
bridge fdb show dev vxlan0 | grep $mac | grep self | grep -q offload
check_err $?
bridge fdb show dev vxlan0 | grep $mac | grep master | grep -q offload
check_err $?
bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
check_err $?
}
check_vxlan_fdb_not_offloaded()
{
local mac=00:11:22:33:44:55
local zmac=00:00:00:00:00:00
bridge fdb show dev vxlan0 | grep $mac | grep -q self
check_err $?
bridge fdb show dev vxlan0 | grep $mac | grep self | grep -q offload
check_fail $?
bridge fdb show dev vxlan0 | grep $zmac | grep -q self
check_err $?
bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
check_fail $?
}
check_bridge_fdb_not_offloaded()
{
local mac=00:11:22:33:44:55
local zmac=00:00:00:00:00:00
bridge fdb show dev vxlan0 | grep $mac | grep -q master
check_err $?
bridge fdb show dev vxlan0 | grep $mac | grep master | grep -q offload
check_fail $?
}
__offload_indication_join_vxlan_first()
{
local vid=$1; shift
local mac=00:11:22:33:44:55
local zmac=00:00:00:00:00:00
bridge fdb append $zmac dev vxlan0 self dst 198.51.100.2
ip link set dev vxlan0 master br0
bridge fdb add dev vxlan0 $mac self master static dst 198.51.100.2
RET=0
check_vxlan_fdb_not_offloaded
ip link set dev $swp1 master br0
sleep .1
check_fdb_offloaded
log_test "offload indication - attach vxlan first"
RET=0
ip link set dev vxlan0 down
check_vxlan_fdb_not_offloaded
check_bridge_fdb_not_offloaded
log_test "offload indication - set vxlan down"
RET=0
ip link set dev vxlan0 up
sleep .1
check_fdb_offloaded
log_test "offload indication - set vxlan up"
if [[ ! -z $vid ]]; then
RET=0
bridge vlan del dev vxlan0 vid $vid
check_vxlan_fdb_not_offloaded
check_bridge_fdb_not_offloaded
log_test "offload indication - delete VLAN"
RET=0
bridge vlan add dev vxlan0 vid $vid
check_vxlan_fdb_not_offloaded
check_bridge_fdb_not_offloaded
log_test "offload indication - add tagged VLAN"
RET=0
bridge vlan add dev vxlan0 vid $vid pvid untagged
sleep .1
check_fdb_offloaded
log_test "offload indication - add pvid/untagged VLAN"
fi
RET=0
ip link set dev $swp1 nomaster
check_vxlan_fdb_not_offloaded
log_test "offload indication - detach port"
}
offload_indication_join_vxlan_first()
{
ip link add dev br0 up type bridge mcast_snooping 0
ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
ttl 20 tos inherit local 198.51.100.1 dstport 4789
__offload_indication_join_vxlan_first
ip link del dev vxlan0
ip link del dev br0
}
__offload_indication_join_vxlan_last()
{
local zmac=00:00:00:00:00:00
RET=0
bridge fdb append $zmac dev vxlan0 self dst 198.51.100.2
ip link set dev $swp1 master br0
bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
check_fail $?
ip link set dev vxlan0 master br0
bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
check_err $?
log_test "offload indication - attach vxlan last"
}
offload_indication_join_vxlan_last()
{
ip link add dev br0 up type bridge mcast_snooping 0
ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
ttl 20 tos inherit local 198.51.100.1 dstport 4789
__offload_indication_join_vxlan_last
ip link del dev vxlan0
ip link del dev br0
}
offload_indication_test() offload_indication_test()
{ {
offload_indication_setup_create offload_indication_setup_create
offload_indication_fdb_test offload_indication_fdb_test
offload_indication_decap_route_test offload_indication_decap_route_test
offload_indication_setup_destroy offload_indication_setup_destroy
log_info "offload indication - replay & cleanup"
offload_indication_join_vxlan_first
offload_indication_join_vxlan_last
} }
sanitization_vlan_aware_test() sanitization_vlan_aware_test()
...@@ -848,12 +995,42 @@ offload_indication_vlan_aware_decap_route_test() ...@@ -848,12 +995,42 @@ offload_indication_vlan_aware_decap_route_test()
log_test "vxlan decap route - vni map/unmap" log_test "vxlan decap route - vni map/unmap"
} }
offload_indication_vlan_aware_join_vxlan_first()
{
ip link add dev br0 up type bridge mcast_snooping 0 \
vlan_filtering 1 vlan_default_pvid 1
ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
ttl 20 tos inherit local 198.51.100.1 dstport 4789
__offload_indication_join_vxlan_first 1
ip link del dev vxlan0
ip link del dev br0
}
offload_indication_vlan_aware_join_vxlan_last()
{
ip link add dev br0 up type bridge mcast_snooping 0 \
vlan_filtering 1 vlan_default_pvid 1
ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
ttl 20 tos inherit local 198.51.100.1 dstport 4789
__offload_indication_join_vxlan_last
ip link del dev vxlan0
ip link del dev br0
}
offload_indication_vlan_aware_test() offload_indication_vlan_aware_test()
{ {
offload_indication_vlan_aware_setup_create offload_indication_vlan_aware_setup_create
offload_indication_vlan_aware_fdb_test offload_indication_vlan_aware_fdb_test
offload_indication_vlan_aware_decap_route_test offload_indication_vlan_aware_decap_route_test
offload_indication_vlan_aware_setup_destroy offload_indication_vlan_aware_setup_destroy
log_info "offload indication - replay & cleanup - vlan aware"
offload_indication_vlan_aware_join_vxlan_first
offload_indication_vlan_aware_join_vxlan_last
} }
trap cleanup EXIT trap cleanup EXIT
......
...@@ -95,6 +95,7 @@ export VXPORT ...@@ -95,6 +95,7 @@ export VXPORT
test_flood test_flood
test_unicast test_unicast
test_learning test_learning
test_pvid
"} "}
NUM_NETIFS=6 NUM_NETIFS=6
...@@ -610,6 +611,75 @@ test_unicast() ...@@ -610,6 +611,75 @@ test_unicast()
done done
} }
test_pvid()
{
local -a expects=(0 0 0 0 0)
local mac=de:ad:be:ef:13:37
local dst=192.0.2.100
local vid=10
# Check that flooding works
RET=0
expects[0]=10; expects[1]=10; expects[3]=10
vxlan_flood_test $mac $dst $vid "${expects[@]}"
log_test "VXLAN: flood before pvid off"
# Toggle PVID off and test that flood to remote hosts does not work
RET=0
bridge vlan add vid 10 dev vx10
expects[0]=10; expects[1]=0; expects[3]=0
vxlan_flood_test $mac $dst $vid "${expects[@]}"
log_test "VXLAN: flood after pvid off"
# Toggle PVID on and test that flood to remote hosts does work
RET=0
bridge vlan add vid 10 dev vx10 pvid untagged
expects[0]=10; expects[1]=10; expects[3]=10
vxlan_flood_test $mac $dst $vid "${expects[@]}"
log_test "VXLAN: flood after pvid on"
# Add a new VLAN and test that it does not affect flooding
RET=0
bridge vlan add vid 30 dev vx10
expects[0]=10; expects[1]=10; expects[3]=10
vxlan_flood_test $mac $dst $vid "${expects[@]}"
bridge vlan del vid 30 dev vx10
log_test "VXLAN: flood after vlan add"
# Remove currently mapped VLAN and test that flood to remote hosts does
# not work
RET=0
bridge vlan del vid 10 dev vx10
expects[0]=10; expects[1]=0; expects[3]=0
vxlan_flood_test $mac $dst $vid "${expects[@]}"
log_test "VXLAN: flood after vlan delete"
# Re-add the VLAN and test that flood to remote hosts does work
RET=0
bridge vlan add vid 10 dev vx10 pvid untagged
expects[0]=10; expects[1]=10; expects[3]=10
vxlan_flood_test $mac $dst $vid "${expects[@]}"
log_test "VXLAN: flood after vlan re-add"
}
vxlan_ping_test() vxlan_ping_test()
{ {
local ping_dev=$1; shift local ping_dev=$1; shift
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment