Skip to content

Commit 1c30d18

Browse files
idoschdavem330
authored andcommitted
mlxsw: spectrum: Enable VxLAN enslavement to bridges
Enslavement of VxLAN devices to offloaded bridges was never forbidden by mlxsw, but this patch makes sure the required configuration is performed in order to allow VxLAN encapsulation and decapsulation to take place in the device. The patch handles both the case where a VxLAN device is enslaved to an already offloaded bridge and the case where the first mlxsw port is enslaved to a bridge that already has VxLAN device configured. Invalid configurations are sanitized and an error string is returned via extack. Since encapsulation and decapsulation do not occur when the VxLAN device is down, the driver makes sure to enable / disable these functionalities based on NETDEV_PRE_UP and NETDEV_DOWN events. Note that NETDEV_PRE_UP is used in favor of NETDEV_UP, as the former allows to veto the operation, if necessary. Signed-off-by: Ido Schimmel <idosch@mellanox.com> Reviewed-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent e9ba0fb commit 1c30d18

File tree

3 files changed

+267
-1
lines changed

3 files changed

+267
-1
lines changed

drivers/net/ethernet/mellanox/mlxsw/spectrum.c

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4587,6 +4587,41 @@ static void mlxsw_sp_port_ovs_leave(struct mlxsw_sp_port *mlxsw_sp_port)
45874587
mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, false);
45884588
}
45894589

4590+
static bool mlxsw_sp_bridge_has_multiple_vxlans(struct net_device *br_dev)
4591+
{
4592+
unsigned int num_vxlans = 0;
4593+
struct net_device *dev;
4594+
struct list_head *iter;
4595+
4596+
netdev_for_each_lower_dev(br_dev, dev, iter) {
4597+
if (netif_is_vxlan(dev))
4598+
num_vxlans++;
4599+
}
4600+
4601+
return num_vxlans > 1;
4602+
}
4603+
4604+
static bool mlxsw_sp_bridge_vxlan_is_valid(struct net_device *br_dev,
4605+
struct netlink_ext_ack *extack)
4606+
{
4607+
if (br_multicast_enabled(br_dev)) {
4608+
NL_SET_ERR_MSG_MOD(extack, "Multicast can not be enabled on a bridge with a VxLAN device");
4609+
return false;
4610+
}
4611+
4612+
if (br_vlan_enabled(br_dev)) {
4613+
NL_SET_ERR_MSG_MOD(extack, "VLAN filtering can not be enabled on a bridge with a VxLAN device");
4614+
return false;
4615+
}
4616+
4617+
if (mlxsw_sp_bridge_has_multiple_vxlans(br_dev)) {
4618+
NL_SET_ERR_MSG_MOD(extack, "Multiple VxLAN devices are not supported in a VLAN-unaware bridge");
4619+
return false;
4620+
}
4621+
4622+
return true;
4623+
}
4624+
45904625
static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
45914626
struct net_device *dev,
45924627
unsigned long event, void *ptr)
@@ -4616,6 +4651,11 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
46164651
}
46174652
if (!info->linking)
46184653
break;
4654+
if (netif_is_bridge_master(upper_dev) &&
4655+
!mlxsw_sp_bridge_device_is_offloaded(mlxsw_sp, upper_dev) &&
4656+
mlxsw_sp_bridge_has_vxlan(upper_dev) &&
4657+
!mlxsw_sp_bridge_vxlan_is_valid(upper_dev, extack))
4658+
return -EOPNOTSUPP;
46194659
if (netdev_has_any_upper_dev(upper_dev) &&
46204660
(!netif_is_bridge_master(upper_dev) ||
46214661
!mlxsw_sp_bridge_device_is_offloaded(mlxsw_sp,
@@ -4773,6 +4813,11 @@ static int mlxsw_sp_netdevice_port_vlan_event(struct net_device *vlan_dev,
47734813
}
47744814
if (!info->linking)
47754815
break;
4816+
if (netif_is_bridge_master(upper_dev) &&
4817+
!mlxsw_sp_bridge_device_is_offloaded(mlxsw_sp, upper_dev) &&
4818+
mlxsw_sp_bridge_has_vxlan(upper_dev) &&
4819+
!mlxsw_sp_bridge_vxlan_is_valid(upper_dev, extack))
4820+
return -EOPNOTSUPP;
47764821
if (netdev_has_any_upper_dev(upper_dev) &&
47774822
(!netif_is_bridge_master(upper_dev) ||
47784823
!mlxsw_sp_bridge_device_is_offloaded(mlxsw_sp,
@@ -4919,6 +4964,63 @@ static bool mlxsw_sp_is_vrf_event(unsigned long event, void *ptr)
49194964
return netif_is_l3_master(info->upper_dev);
49204965
}
49214966

4967+
static int mlxsw_sp_netdevice_vxlan_event(struct mlxsw_sp *mlxsw_sp,
4968+
struct net_device *dev,
4969+
unsigned long event, void *ptr)
4970+
{
4971+
struct netdev_notifier_changeupper_info *cu_info;
4972+
struct netdev_notifier_info *info = ptr;
4973+
struct netlink_ext_ack *extack;
4974+
struct net_device *upper_dev;
4975+
4976+
extack = netdev_notifier_info_to_extack(info);
4977+
4978+
switch (event) {
4979+
case NETDEV_CHANGEUPPER:
4980+
cu_info = container_of(info,
4981+
struct netdev_notifier_changeupper_info,
4982+
info);
4983+
upper_dev = cu_info->upper_dev;
4984+
if (!netif_is_bridge_master(upper_dev))
4985+
return 0;
4986+
if (!mlxsw_sp_lower_get(upper_dev))
4987+
return 0;
4988+
if (!mlxsw_sp_bridge_vxlan_is_valid(upper_dev, extack))
4989+
return -EOPNOTSUPP;
4990+
if (cu_info->linking) {
4991+
if (!netif_running(dev))
4992+
return 0;
4993+
return mlxsw_sp_bridge_vxlan_join(mlxsw_sp, upper_dev,
4994+
dev, extack);
4995+
} else {
4996+
mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, upper_dev, dev);
4997+
}
4998+
break;
4999+
case NETDEV_PRE_UP:
5000+
upper_dev = netdev_master_upper_dev_get(dev);
5001+
if (!upper_dev)
5002+
return 0;
5003+
if (!netif_is_bridge_master(upper_dev))
5004+
return 0;
5005+
if (!mlxsw_sp_lower_get(upper_dev))
5006+
return 0;
5007+
return mlxsw_sp_bridge_vxlan_join(mlxsw_sp, upper_dev, dev,
5008+
extack);
5009+
case NETDEV_DOWN:
5010+
upper_dev = netdev_master_upper_dev_get(dev);
5011+
if (!upper_dev)
5012+
return 0;
5013+
if (!netif_is_bridge_master(upper_dev))
5014+
return 0;
5015+
if (!mlxsw_sp_lower_get(upper_dev))
5016+
return 0;
5017+
mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, upper_dev, dev);
5018+
break;
5019+
}
5020+
5021+
return 0;
5022+
}
5023+
49225024
static int mlxsw_sp_netdevice_event(struct notifier_block *nb,
49235025
unsigned long event, void *ptr)
49245026
{
@@ -4935,6 +5037,8 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *nb,
49355037
}
49365038
mlxsw_sp_span_respin(mlxsw_sp);
49375039

5040+
if (netif_is_vxlan(dev))
5041+
err = mlxsw_sp_netdevice_vxlan_event(mlxsw_sp, dev, event, ptr);
49385042
if (mlxsw_sp_netdev_is_ipip_ol(mlxsw_sp, dev))
49395043
err = mlxsw_sp_netdevice_ipip_ol_event(mlxsw_sp, dev,
49405044
event, ptr);

drivers/net/ethernet/mellanox/mlxsw/spectrum.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <net/psample.h>
1717
#include <net/pkt_cls.h>
1818
#include <net/red.h>
19+
#include <net/vxlan.h>
1920

2021
#include "port.h"
2122
#include "core.h"
@@ -241,6 +242,25 @@ struct mlxsw_sp_port {
241242
struct mlxsw_sp_acl_block *eg_acl_block;
242243
};
243244

245+
static inline struct net_device *
246+
mlxsw_sp_bridge_vxlan_dev_find(struct net_device *br_dev)
247+
{
248+
struct net_device *dev;
249+
struct list_head *iter;
250+
251+
netdev_for_each_lower_dev(br_dev, dev, iter) {
252+
if (netif_is_vxlan(dev))
253+
return dev;
254+
}
255+
256+
return NULL;
257+
}
258+
259+
static inline bool mlxsw_sp_bridge_has_vxlan(struct net_device *br_dev)
260+
{
261+
return !!mlxsw_sp_bridge_vxlan_dev_find(br_dev);
262+
}
263+
244264
static inline bool
245265
mlxsw_sp_port_is_pause_en(const struct mlxsw_sp_port *mlxsw_sp_port)
246266
{
@@ -336,6 +356,13 @@ void mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port,
336356
struct net_device *br_dev);
337357
bool mlxsw_sp_bridge_device_is_offloaded(const struct mlxsw_sp *mlxsw_sp,
338358
const struct net_device *br_dev);
359+
int mlxsw_sp_bridge_vxlan_join(struct mlxsw_sp *mlxsw_sp,
360+
const struct net_device *br_dev,
361+
const struct net_device *vxlan_dev,
362+
struct netlink_ext_ack *extack);
363+
void mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp,
364+
const struct net_device *br_dev,
365+
const struct net_device *vxlan_dev);
339366

340367
/* spectrum.c */
341368
int mlxsw_sp_port_ets_set(struct mlxsw_sp_port *mlxsw_sp_port,

drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/rtnetlink.h>
1616
#include <linux/netlink.h>
1717
#include <net/switchdev.h>
18+
#include <net/vxlan.h>
1819

1920
#include "spectrum_span.h"
2021
#include "spectrum_switchdev.h"
@@ -83,6 +84,11 @@ struct mlxsw_sp_bridge_ops {
8384
void (*port_leave)(struct mlxsw_sp_bridge_device *bridge_device,
8485
struct mlxsw_sp_bridge_port *bridge_port,
8586
struct mlxsw_sp_port *mlxsw_sp_port);
87+
int (*vxlan_join)(struct mlxsw_sp_bridge_device *bridge_device,
88+
const struct net_device *vxlan_dev,
89+
struct netlink_ext_ack *extack);
90+
void (*vxlan_leave)(struct mlxsw_sp_bridge_device *bridge_device,
91+
const struct net_device *vxlan_dev);
8692
struct mlxsw_sp_fid *
8793
(*fid_get)(struct mlxsw_sp_bridge_device *bridge_device,
8894
u16 vid);
@@ -1949,6 +1955,21 @@ mlxsw_sp_bridge_8021q_port_leave(struct mlxsw_sp_bridge_device *bridge_device,
19491955
mlxsw_sp_port_pvid_set(mlxsw_sp_port, 1);
19501956
}
19511957

1958+
static int
1959+
mlxsw_sp_bridge_8021q_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
1960+
const struct net_device *vxlan_dev,
1961+
struct netlink_ext_ack *extack)
1962+
{
1963+
WARN_ON(1);
1964+
return -EINVAL;
1965+
}
1966+
1967+
static void
1968+
mlxsw_sp_bridge_8021q_vxlan_leave(struct mlxsw_sp_bridge_device *bridge_device,
1969+
const struct net_device *vxlan_dev)
1970+
{
1971+
}
1972+
19521973
static struct mlxsw_sp_fid *
19531974
mlxsw_sp_bridge_8021q_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
19541975
u16 vid)
@@ -1961,6 +1982,8 @@ mlxsw_sp_bridge_8021q_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
19611982
static const struct mlxsw_sp_bridge_ops mlxsw_sp_bridge_8021q_ops = {
19621983
.port_join = mlxsw_sp_bridge_8021q_port_join,
19631984
.port_leave = mlxsw_sp_bridge_8021q_port_leave,
1985+
.vxlan_join = mlxsw_sp_bridge_8021q_vxlan_join,
1986+
.vxlan_leave = mlxsw_sp_bridge_8021q_vxlan_leave,
19641987
.fid_get = mlxsw_sp_bridge_8021q_fid_get,
19651988
};
19661989

@@ -2025,18 +2048,103 @@ mlxsw_sp_bridge_8021d_port_leave(struct mlxsw_sp_bridge_device *bridge_device,
20252048
mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan);
20262049
}
20272050

2051+
static int
2052+
mlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
2053+
const struct net_device *vxlan_dev,
2054+
struct netlink_ext_ack *extack)
2055+
{
2056+
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
2057+
struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
2058+
struct mlxsw_sp_nve_params params = {
2059+
.type = MLXSW_SP_NVE_TYPE_VXLAN,
2060+
.vni = vxlan->cfg.vni,
2061+
.dev = vxlan_dev,
2062+
};
2063+
struct mlxsw_sp_fid *fid;
2064+
int err;
2065+
2066+
fid = mlxsw_sp_fid_8021d_lookup(mlxsw_sp, bridge_device->dev->ifindex);
2067+
if (!fid)
2068+
return -EINVAL;
2069+
2070+
if (mlxsw_sp_fid_vni_is_set(fid))
2071+
return -EINVAL;
2072+
2073+
err = mlxsw_sp_nve_fid_enable(mlxsw_sp, fid, &params, extack);
2074+
if (err)
2075+
goto err_nve_fid_enable;
2076+
2077+
/* The tunnel port does not hold a reference on the FID. Only
2078+
* local ports and the router port
2079+
*/
2080+
mlxsw_sp_fid_put(fid);
2081+
2082+
return 0;
2083+
2084+
err_nve_fid_enable:
2085+
mlxsw_sp_fid_put(fid);
2086+
return err;
2087+
}
2088+
2089+
static void
2090+
mlxsw_sp_bridge_8021d_vxlan_leave(struct mlxsw_sp_bridge_device *bridge_device,
2091+
const struct net_device *vxlan_dev)
2092+
{
2093+
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
2094+
struct mlxsw_sp_fid *fid;
2095+
2096+
fid = mlxsw_sp_fid_8021d_lookup(mlxsw_sp, bridge_device->dev->ifindex);
2097+
if (WARN_ON(!fid))
2098+
return;
2099+
2100+
/* If the VxLAN device is down, then the FID does not have a VNI */
2101+
if (!mlxsw_sp_fid_vni_is_set(fid))
2102+
goto out;
2103+
2104+
mlxsw_sp_nve_fid_disable(mlxsw_sp, fid);
2105+
out:
2106+
mlxsw_sp_fid_put(fid);
2107+
}
2108+
20282109
static struct mlxsw_sp_fid *
20292110
mlxsw_sp_bridge_8021d_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
20302111
u16 vid)
20312112
{
20322113
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
2114+
struct net_device *vxlan_dev;
2115+
struct mlxsw_sp_fid *fid;
2116+
int err;
20332117

2034-
return mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device->dev->ifindex);
2118+
fid = mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device->dev->ifindex);
2119+
if (IS_ERR(fid))
2120+
return fid;
2121+
2122+
if (mlxsw_sp_fid_vni_is_set(fid))
2123+
return fid;
2124+
2125+
vxlan_dev = mlxsw_sp_bridge_vxlan_dev_find(bridge_device->dev);
2126+
if (!vxlan_dev)
2127+
return fid;
2128+
2129+
if (!netif_running(vxlan_dev))
2130+
return fid;
2131+
2132+
err = mlxsw_sp_bridge_8021d_vxlan_join(bridge_device, vxlan_dev, NULL);
2133+
if (err)
2134+
goto err_vxlan_join;
2135+
2136+
return fid;
2137+
2138+
err_vxlan_join:
2139+
mlxsw_sp_fid_put(fid);
2140+
return ERR_PTR(err);
20352141
}
20362142

20372143
static const struct mlxsw_sp_bridge_ops mlxsw_sp_bridge_8021d_ops = {
20382144
.port_join = mlxsw_sp_bridge_8021d_port_join,
20392145
.port_leave = mlxsw_sp_bridge_8021d_port_leave,
2146+
.vxlan_join = mlxsw_sp_bridge_8021d_vxlan_join,
2147+
.vxlan_leave = mlxsw_sp_bridge_8021d_vxlan_leave,
20402148
.fid_get = mlxsw_sp_bridge_8021d_fid_get,
20412149
};
20422150

@@ -2087,6 +2195,33 @@ void mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port,
20872195
mlxsw_sp_bridge_port_put(mlxsw_sp->bridge, bridge_port);
20882196
}
20892197

2198+
int mlxsw_sp_bridge_vxlan_join(struct mlxsw_sp *mlxsw_sp,
2199+
const struct net_device *br_dev,
2200+
const struct net_device *vxlan_dev,
2201+
struct netlink_ext_ack *extack)
2202+
{
2203+
struct mlxsw_sp_bridge_device *bridge_device;
2204+
2205+
bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
2206+
if (WARN_ON(!bridge_device))
2207+
return -EINVAL;
2208+
2209+
return bridge_device->ops->vxlan_join(bridge_device, vxlan_dev, extack);
2210+
}
2211+
2212+
void mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp,
2213+
const struct net_device *br_dev,
2214+
const struct net_device *vxlan_dev)
2215+
{
2216+
struct mlxsw_sp_bridge_device *bridge_device;
2217+
2218+
bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
2219+
if (WARN_ON(!bridge_device))
2220+
return;
2221+
2222+
bridge_device->ops->vxlan_leave(bridge_device, vxlan_dev);
2223+
}
2224+
20902225
static void
20912226
mlxsw_sp_fdb_call_notifiers(enum switchdev_notifier_type type,
20922227
const char *mac, u16 vid,

0 commit comments

Comments
 (0)