Skip to content

Commit 382d7dc

Browse files
committed
Merge branch 'mlxsw-rif-pvid'
Petr Machata says: ==================== mlxsw: Manage RIF across PVID changes The mlxsw driver currently makes the assumption that the user applies configuration in a bottom-up manner. Thus netdevices need to be added to the bridge before IP addresses are configured on that bridge or SVI added on top of it. Enslaving a netdevice to another netdevice that already has uppers is in fact forbidden by mlxsw for this reason. Despite this safety, it is rather easy to get into situations where the offloaded configuration is just plain wrong. As an example, take a front panel port, configure an IP address: it gets a RIF. Now enslave the port to the bridge, and the RIF is gone. Remove the port from the bridge again, but the RIF never comes back. There is a number of similar situations, where changing the configuration there and back utterly breaks the offload. The situation is going to be made better by implementing a range of replays and post-hoc offloads. In this patch set, address the ordering issues related to creation of bridge RIFs. Currently, mlxsw has several shortcomings with regards to RIF handling due to PVID changes: - In order to cause RIF for a bridge device to be created, the user is expected first to set PVID, then to add an IP address. The reverse ordering is disallowed, which is not very user-friendly. - When such bridge gets a VLAN upper whose VID was the same as the existing PVID, and this VLAN netdevice gets an IP address, a RIF is created for this netdevice. The new RIF is then assigned to the 802.1Q FID for the given VID. This results in a working configuration. However, then, when the VLAN netdevice is removed again, the RIF for the bridge itself is never reassociated to the PVID. - PVID cannot be changed once the bridge has uppers. Presumably this is because the driver does not manage RIFs properly in face of PVID changes. However, as the previous point shows, it is still possible to get into invalid configurations. This patch set addresses these issues and relaxes some of the ordering requirements that mlxsw had. The patch set proceeds as follows: - In patch #1, pass extack to mlxsw_sp_br_ban_rif_pvid_change() - To relax ordering between setting PVID and adding an IP address to a bridge, mlxsw must be able to request that a RIF is created with a given VLAN ID, instead of trying to deduce it from the current netdevice settings, which do not reflect the user-requested values yet. This is done in patches #2 and #3. - Similarly, mlxsw_sp_inetaddr_bridge_event() will need to make decisions based on the user-requested value of PVID, not the current value. Thus in patches #4 and #5, add a new argument which carries the requested PVID value. - Finally in patch #6 relax the ban on PVID changes when a bridge has uppers. Instead, add the logic necessary for creation of a RIF as a result of PVID change. - Relevant selftests are presented afterwards. In patch #7 a preparatory helper is added to lib.sh. Patches #8, #9, #10 and #11 include selftests themselves. ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents f16276a + 9cbb3da commit 382d7dc

File tree

9 files changed

+643
-58
lines changed

9 files changed

+643
-58
lines changed

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

Lines changed: 149 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ struct mlxsw_sp_rif_ops {
139139
struct netlink_ext_ack *extack);
140140
void (*deconfigure)(struct mlxsw_sp_rif *rif);
141141
struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif,
142+
const struct mlxsw_sp_rif_params *params,
142143
struct netlink_ext_ack *extack);
143144
void (*fdb_del)(struct mlxsw_sp_rif *rif, const char *mac);
144145
};
@@ -8300,7 +8301,7 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
83008301
rif->rif_entries = rif_entries;
83018302

83028303
if (ops->fid_get) {
8303-
fid = ops->fid_get(rif, extack);
8304+
fid = ops->fid_get(rif, params, extack);
83048305
if (IS_ERR(fid)) {
83058306
err = PTR_ERR(fid);
83068307
goto err_fid_get;
@@ -8410,6 +8411,110 @@ void mlxsw_sp_rif_destroy_by_dev(struct mlxsw_sp *mlxsw_sp,
84108411
mutex_unlock(&mlxsw_sp->router->lock);
84118412
}
84128413

8414+
static void mlxsw_sp_rif_destroy_vlan_upper(struct mlxsw_sp *mlxsw_sp,
8415+
struct net_device *br_dev,
8416+
u16 vid)
8417+
{
8418+
struct net_device *upper_dev;
8419+
struct mlxsw_sp_crif *crif;
8420+
8421+
rcu_read_lock();
8422+
upper_dev = __vlan_find_dev_deep_rcu(br_dev, htons(ETH_P_8021Q), vid);
8423+
rcu_read_unlock();
8424+
8425+
if (!upper_dev)
8426+
return;
8427+
8428+
crif = mlxsw_sp_crif_lookup(mlxsw_sp->router, upper_dev);
8429+
if (!crif || !crif->rif)
8430+
return;
8431+
8432+
mlxsw_sp_rif_destroy(crif->rif);
8433+
}
8434+
8435+
static int mlxsw_sp_inetaddr_bridge_event(struct mlxsw_sp *mlxsw_sp,
8436+
struct net_device *l3_dev,
8437+
int lower_pvid,
8438+
unsigned long event,
8439+
struct netlink_ext_ack *extack);
8440+
8441+
int mlxsw_sp_router_bridge_vlan_add(struct mlxsw_sp *mlxsw_sp,
8442+
struct net_device *br_dev,
8443+
u16 new_vid, bool is_pvid,
8444+
struct netlink_ext_ack *extack)
8445+
{
8446+
struct mlxsw_sp_rif *old_rif;
8447+
struct mlxsw_sp_rif *new_rif;
8448+
struct net_device *upper_dev;
8449+
u16 old_pvid = 0;
8450+
u16 new_pvid;
8451+
int err = 0;
8452+
8453+
mutex_lock(&mlxsw_sp->router->lock);
8454+
old_rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, br_dev);
8455+
if (old_rif) {
8456+
/* If the RIF on the bridge is not a VLAN RIF, we shouldn't have
8457+
* gotten a PVID notification.
8458+
*/
8459+
if (WARN_ON(old_rif->ops->type != MLXSW_SP_RIF_TYPE_VLAN))
8460+
old_rif = NULL;
8461+
else
8462+
old_pvid = mlxsw_sp_fid_8021q_vid(old_rif->fid);
8463+
}
8464+
8465+
if (is_pvid)
8466+
new_pvid = new_vid;
8467+
else if (old_pvid == new_vid)
8468+
new_pvid = 0;
8469+
else
8470+
goto out;
8471+
8472+
if (old_pvid == new_pvid)
8473+
goto out;
8474+
8475+
if (new_pvid) {
8476+
struct mlxsw_sp_rif_params params = {
8477+
.dev = br_dev,
8478+
.vid = new_pvid,
8479+
};
8480+
8481+
/* If there is a VLAN upper with the same VID as the new PVID,
8482+
* kill its RIF, if there is one.
8483+
*/
8484+
mlxsw_sp_rif_destroy_vlan_upper(mlxsw_sp, br_dev, new_pvid);
8485+
8486+
if (mlxsw_sp_dev_addr_list_empty(br_dev))
8487+
goto out;
8488+
new_rif = mlxsw_sp_rif_create(mlxsw_sp, &params, extack);
8489+
if (IS_ERR(new_rif)) {
8490+
err = PTR_ERR(new_rif);
8491+
goto out;
8492+
}
8493+
8494+
if (old_pvid)
8495+
mlxsw_sp_rif_migrate_destroy(mlxsw_sp, old_rif, new_rif,
8496+
true);
8497+
} else {
8498+
mlxsw_sp_rif_destroy(old_rif);
8499+
}
8500+
8501+
if (old_pvid) {
8502+
rcu_read_lock();
8503+
upper_dev = __vlan_find_dev_deep_rcu(br_dev, htons(ETH_P_8021Q),
8504+
old_pvid);
8505+
rcu_read_unlock();
8506+
if (upper_dev)
8507+
err = mlxsw_sp_inetaddr_bridge_event(mlxsw_sp,
8508+
upper_dev,
8509+
new_pvid,
8510+
NETDEV_UP, extack);
8511+
}
8512+
8513+
out:
8514+
mutex_unlock(&mlxsw_sp->router->lock);
8515+
return err;
8516+
}
8517+
84138518
static void
84148519
mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
84158520
struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
@@ -8664,21 +8769,24 @@ __mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
86648769
{
86658770
struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
86668771
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
8667-
struct mlxsw_sp_rif_params params = {
8668-
.dev = l3_dev,
8669-
};
8772+
struct mlxsw_sp_rif_params params;
86708773
u16 vid = mlxsw_sp_port_vlan->vid;
86718774
struct mlxsw_sp_rif *rif;
86728775
struct mlxsw_sp_fid *fid;
86738776
int err;
86748777

8778+
params = (struct mlxsw_sp_rif_params) {
8779+
.dev = l3_dev,
8780+
.vid = vid,
8781+
};
8782+
86758783
mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
86768784
rif = mlxsw_sp_rif_subport_get(mlxsw_sp, &params, extack);
86778785
if (IS_ERR(rif))
86788786
return PTR_ERR(rif);
86798787

86808788
/* FID was already created, just take a reference */
8681-
fid = rif->ops->fid_get(rif, extack);
8789+
fid = rif->ops->fid_get(rif, &params, extack);
86828790
err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
86838791
if (err)
86848792
goto err_fid_port_vid_map;
@@ -8822,13 +8930,15 @@ static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
88228930

88238931
static int mlxsw_sp_inetaddr_bridge_event(struct mlxsw_sp *mlxsw_sp,
88248932
struct net_device *l3_dev,
8933+
int lower_pvid,
88258934
unsigned long event,
88268935
struct netlink_ext_ack *extack)
88278936
{
88288937
struct mlxsw_sp_rif_params params = {
88298938
.dev = l3_dev,
88308939
};
88318940
struct mlxsw_sp_rif *rif;
8941+
int err;
88328942

88338943
switch (event) {
88348944
case NETDEV_UP:
@@ -8840,7 +8950,21 @@ static int mlxsw_sp_inetaddr_bridge_event(struct mlxsw_sp *mlxsw_sp,
88408950
NL_SET_ERR_MSG_MOD(extack, "Adding an IP address to 802.1ad bridge is not supported");
88418951
return -EOPNOTSUPP;
88428952
}
8953+
err = br_vlan_get_pvid(l3_dev, &params.vid);
8954+
if (err)
8955+
return err;
8956+
if (!params.vid)
8957+
return 0;
8958+
} else if (is_vlan_dev(l3_dev)) {
8959+
params.vid = vlan_dev_vlan_id(l3_dev);
8960+
8961+
/* If the VID matches PVID of the bridge below, the
8962+
* bridge owns the RIF for this VLAN. Don't do anything.
8963+
*/
8964+
if ((int)params.vid == lower_pvid)
8965+
return 0;
88438966
}
8967+
88448968
rif = mlxsw_sp_rif_create(mlxsw_sp, &params, extack);
88458969
if (IS_ERR(rif))
88468970
return PTR_ERR(rif);
@@ -8861,19 +8985,27 @@ static int mlxsw_sp_inetaddr_vlan_event(struct mlxsw_sp *mlxsw_sp,
88618985
{
88628986
struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
88638987
u16 vid = vlan_dev_vlan_id(vlan_dev);
8988+
u16 lower_pvid;
8989+
int err;
88648990

88658991
if (netif_is_bridge_port(vlan_dev))
88668992
return 0;
88678993

8868-
if (mlxsw_sp_port_dev_check(real_dev))
8994+
if (mlxsw_sp_port_dev_check(real_dev)) {
88698995
return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
88708996
event, vid, extack);
8871-
else if (netif_is_lag_master(real_dev))
8997+
} else if (netif_is_lag_master(real_dev)) {
88728998
return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
88738999
vid, extack);
8874-
else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
8875-
return mlxsw_sp_inetaddr_bridge_event(mlxsw_sp, vlan_dev, event,
9000+
} else if (netif_is_bridge_master(real_dev) &&
9001+
br_vlan_enabled(real_dev)) {
9002+
err = br_vlan_get_pvid(real_dev, &lower_pvid);
9003+
if (err)
9004+
return err;
9005+
return mlxsw_sp_inetaddr_bridge_event(mlxsw_sp, vlan_dev,
9006+
lower_pvid, event,
88769007
extack);
9008+
}
88779009

88789010
return 0;
88799011
}
@@ -9008,7 +9140,7 @@ static int __mlxsw_sp_inetaddr_event(struct mlxsw_sp *mlxsw_sp,
90089140
else if (netif_is_lag_master(dev))
90099141
return mlxsw_sp_inetaddr_lag_event(dev, event, extack);
90109142
else if (netif_is_bridge_master(dev))
9011-
return mlxsw_sp_inetaddr_bridge_event(mlxsw_sp, dev, event,
9143+
return mlxsw_sp_inetaddr_bridge_event(mlxsw_sp, dev, -1, event,
90129144
extack);
90139145
else if (is_vlan_dev(dev))
90149146
return mlxsw_sp_inetaddr_vlan_event(mlxsw_sp, dev, event,
@@ -9724,6 +9856,7 @@ static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
97249856

97259857
static struct mlxsw_sp_fid *
97269858
mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif,
9859+
const struct mlxsw_sp_rif_params *params,
97279860
struct netlink_ext_ack *extack)
97289861
{
97299862
return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
@@ -9836,6 +9969,7 @@ static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
98369969

98379970
static struct mlxsw_sp_fid *
98389971
mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif,
9972+
const struct mlxsw_sp_rif_params *params,
98399973
struct netlink_ext_ack *extack)
98409974
{
98419975
int rif_ifindex = mlxsw_sp_rif_dev_ifindex(rif);
@@ -9869,27 +10003,22 @@ static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
986910003

987010004
static struct mlxsw_sp_fid *
987110005
mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif,
10006+
const struct mlxsw_sp_rif_params *params,
987210007
struct netlink_ext_ack *extack)
987310008
{
987410009
struct net_device *dev = mlxsw_sp_rif_dev(rif);
987510010
struct net_device *br_dev;
9876-
u16 vid;
9877-
int err;
10011+
10012+
if (WARN_ON(!params->vid))
10013+
return ERR_PTR(-EINVAL);
987810014

987910015
if (is_vlan_dev(dev)) {
9880-
vid = vlan_dev_vlan_id(dev);
988110016
br_dev = vlan_dev_real_dev(dev);
988210017
if (WARN_ON(!netif_is_bridge_master(br_dev)))
988310018
return ERR_PTR(-EINVAL);
9884-
} else {
9885-
err = br_vlan_get_pvid(dev, &vid);
9886-
if (err < 0 || !vid) {
9887-
NL_SET_ERR_MSG_MOD(extack, "Couldn't determine bridge PVID");
9888-
return ERR_PTR(-EINVAL);
9889-
}
989010019
}
989110020

9892-
return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
10021+
return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, params->vid);
989310022
}
989410023

989510024
static void mlxsw_sp_rif_vlan_fdb_del(struct mlxsw_sp_rif *rif, const char *mac)

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ int mlxsw_sp_ipip_ecn_encap_init(struct mlxsw_sp *mlxsw_sp);
171171
int mlxsw_sp_ipip_ecn_decap_init(struct mlxsw_sp *mlxsw_sp);
172172
struct net_device *
173173
mlxsw_sp_ipip_netdev_ul_dev_get(const struct net_device *ol_dev);
174+
int mlxsw_sp_router_bridge_vlan_add(struct mlxsw_sp *mlxsw_sp,
175+
struct net_device *dev,
176+
u16 new_vid, bool is_pvid,
177+
struct netlink_ext_ack *extack);
174178
int mlxsw_sp_router_port_join_lag(struct mlxsw_sp_port *mlxsw_sp_port,
175179
struct net_device *lag_dev,
176180
struct netlink_ext_ack *extack);

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

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,29 +1479,15 @@ mlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port,
14791479
}
14801480

14811481
static int
1482-
mlxsw_sp_br_ban_rif_pvid_change(struct mlxsw_sp *mlxsw_sp,
1483-
const struct net_device *br_dev,
1484-
const struct switchdev_obj_port_vlan *vlan)
1482+
mlxsw_sp_br_rif_pvid_change(struct mlxsw_sp *mlxsw_sp,
1483+
struct net_device *br_dev,
1484+
const struct switchdev_obj_port_vlan *vlan,
1485+
struct netlink_ext_ack *extack)
14851486
{
1486-
u16 pvid;
1487-
1488-
pvid = mlxsw_sp_rif_vid(mlxsw_sp, br_dev);
1489-
if (!pvid)
1490-
return 0;
1491-
1492-
if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
1493-
if (vlan->vid != pvid) {
1494-
netdev_err(br_dev, "Can't change PVID, it's used by router interface\n");
1495-
return -EBUSY;
1496-
}
1497-
} else {
1498-
if (vlan->vid == pvid) {
1499-
netdev_err(br_dev, "Can't remove PVID, it's used by router interface\n");
1500-
return -EBUSY;
1501-
}
1502-
}
1487+
bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
15031488

1504-
return 0;
1489+
return mlxsw_sp_router_bridge_vlan_add(mlxsw_sp, br_dev, vlan->vid,
1490+
flag_pvid, extack);
15051491
}
15061492

15071493
static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
@@ -1518,8 +1504,8 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
15181504
int err = 0;
15191505

15201506
if (br_vlan_enabled(orig_dev))
1521-
err = mlxsw_sp_br_ban_rif_pvid_change(mlxsw_sp,
1522-
orig_dev, vlan);
1507+
err = mlxsw_sp_br_rif_pvid_change(mlxsw_sp, orig_dev,
1508+
vlan, extack);
15231509
if (!err)
15241510
err = -EOPNOTSUPP;
15251511
return err;

tools/testing/selftests/net/forwarding/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ TEST_PROGS = bridge_igmp.sh \
6565
q_in_vni.sh \
6666
router_bridge.sh \
6767
router_bridge_vlan.sh \
68+
router_bridge_pvid_vlan_upper.sh \
69+
router_bridge_vlan_upper_pvid.sh \
6870
router_broadcast.sh \
6971
router_mpath_nh_res.sh \
7072
router_mpath_nh.sh \

tools/testing/selftests/net/forwarding/lib.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,15 @@ ping_test()
12151215
log_test "ping$3"
12161216
}
12171217

1218+
ping_test_fails()
1219+
{
1220+
RET=0
1221+
1222+
ping_do $1 $2
1223+
check_fail $?
1224+
log_test "ping fails$3"
1225+
}
1226+
12181227
ping6_do()
12191228
{
12201229
local if_name=$1
@@ -1237,6 +1246,15 @@ ping6_test()
12371246
log_test "ping6$3"
12381247
}
12391248

1249+
ping6_test_fails()
1250+
{
1251+
RET=0
1252+
1253+
ping6_do $1 $2
1254+
check_fail $?
1255+
log_test "ping6 fails$3"
1256+
}
1257+
12401258
learning_test()
12411259
{
12421260
local bridge=$1

0 commit comments

Comments
 (0)