Commit 790a9a7c authored by David S. Miller's avatar David S. Miller

Merge branch 'mlxsw-Reduce-dependency-between-bridge-and-router-code'

Ido Schimmel says:

====================
mlxsw: Reduce dependency between bridge and router code

This patch set reduces the dependency between the bridge and the router
code in preparation for RTNL removal from the route insertion path in
mlxsw.

The motivation and solution are explained in detail in patch #3. The
main idea is that we need to stop special-casing the VXLAN devices with
regards to the reference counting of the FIDs. Otherwise, we can bump
into the situation described in patch #3, where the routing code calls
into the bridge code which calls back into the routing code. After
adding a mutex to protect router data structures to remove RTNL
dependency, this can result in an AA deadlock.

Patches #1 and #2 are preparations. They convert the FIDs to use
'refcount_t' for reference counting in order to catch over/under flows
and add extack to the bridge creation function.

Patches #3-#5 reduce the dependency between the bridge and the router
code. First, by having the VXLAN device take a reference on the FID in
patch #3 and then by removing unnecessary code following the change in
patch #3.

Patches #6-#10 adjust existing selftests and add new ones to exercise
the new code paths.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 583cb0b4 495c3da6
......@@ -468,10 +468,6 @@ int mlxsw_sp_bridge_vxlan_join(struct mlxsw_sp *mlxsw_sp,
struct netlink_ext_ack *extack);
void mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp,
const struct net_device *vxlan_dev);
struct mlxsw_sp_fid *mlxsw_sp_bridge_fid_get(struct mlxsw_sp *mlxsw_sp,
const struct net_device *br_dev,
u16 vid,
struct netlink_ext_ack *extack);
extern struct notifier_block mlxsw_sp_switchdev_notifier;
/* spectrum.c */
......
......@@ -8,6 +8,7 @@
#include <linux/netdevice.h>
#include <linux/rhashtable.h>
#include <linux/rtnetlink.h>
#include <linux/refcount.h>
#include "spectrum.h"
#include "reg.h"
......@@ -24,7 +25,7 @@ struct mlxsw_sp_fid_core {
struct mlxsw_sp_fid {
struct list_head list;
struct mlxsw_sp_rif *rif;
unsigned int ref_count;
refcount_t ref_count;
u16 fid_index;
struct mlxsw_sp_fid_family *fid_family;
struct rhash_head ht_node;
......@@ -149,7 +150,7 @@ struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_index(struct mlxsw_sp *mlxsw_sp,
fid = rhashtable_lookup_fast(&mlxsw_sp->fid_core->fid_ht, &fid_index,
mlxsw_sp_fid_ht_params);
if (fid)
fid->ref_count++;
refcount_inc(&fid->ref_count);
return fid;
}
......@@ -183,7 +184,7 @@ struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_vni(struct mlxsw_sp *mlxsw_sp,
fid = rhashtable_lookup_fast(&mlxsw_sp->fid_core->vni_ht, &vni,
mlxsw_sp_fid_vni_ht_params);
if (fid)
fid->ref_count++;
refcount_inc(&fid->ref_count);
return fid;
}
......@@ -1030,7 +1031,7 @@ static struct mlxsw_sp_fid *mlxsw_sp_fid_lookup(struct mlxsw_sp *mlxsw_sp,
list_for_each_entry(fid, &fid_family->fids_list, list) {
if (!fid->fid_family->ops->compare(fid, arg))
continue;
fid->ref_count++;
refcount_inc(&fid->ref_count);
return fid;
}
......@@ -1075,7 +1076,7 @@ static struct mlxsw_sp_fid *mlxsw_sp_fid_get(struct mlxsw_sp *mlxsw_sp,
goto err_rhashtable_insert;
list_add(&fid->list, &fid_family->fids_list);
fid->ref_count++;
refcount_set(&fid->ref_count, 1);
return fid;
err_rhashtable_insert:
......@@ -1093,7 +1094,7 @@ void mlxsw_sp_fid_put(struct mlxsw_sp_fid *fid)
struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp;
if (--fid->ref_count != 0)
if (!refcount_dec_and_test(&fid->ref_count))
return;
list_del(&fid->list);
......
......@@ -7428,7 +7428,7 @@ mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif,
}
}
return mlxsw_sp_bridge_fid_get(rif->mlxsw_sp, br_dev, vid, extack);
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)
......@@ -7519,7 +7519,7 @@ static struct mlxsw_sp_fid *
mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif,
struct netlink_ext_ack *extack)
{
return mlxsw_sp_bridge_fid_get(rif->mlxsw_sp, rif->dev, 0, extack);
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)
......
......@@ -153,16 +153,64 @@ static void mlxsw_sp_bridge_device_rifs_destroy(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp);
}
static int mlxsw_sp_bridge_device_vxlan_init(struct mlxsw_sp_bridge *bridge,
struct net_device *br_dev,
struct netlink_ext_ack *extack)
{
struct net_device *dev, *stop_dev;
struct list_head *iter;
int err;
netdev_for_each_lower_dev(br_dev, dev, iter) {
if (netif_is_vxlan(dev) && netif_running(dev)) {
err = mlxsw_sp_bridge_vxlan_join(bridge->mlxsw_sp,
br_dev, dev, 0,
extack);
if (err) {
stop_dev = dev;
goto err_vxlan_join;
}
}
}
return 0;
err_vxlan_join:
netdev_for_each_lower_dev(br_dev, dev, iter) {
if (netif_is_vxlan(dev) && netif_running(dev)) {
if (stop_dev == dev)
break;
mlxsw_sp_bridge_vxlan_leave(bridge->mlxsw_sp, dev);
}
}
return err;
}
static void mlxsw_sp_bridge_device_vxlan_fini(struct mlxsw_sp_bridge *bridge,
struct net_device *br_dev)
{
struct net_device *dev;
struct list_head *iter;
netdev_for_each_lower_dev(br_dev, dev, iter) {
if (netif_is_vxlan(dev) && netif_running(dev))
mlxsw_sp_bridge_vxlan_leave(bridge->mlxsw_sp, dev);
}
}
static struct mlxsw_sp_bridge_device *
mlxsw_sp_bridge_device_create(struct mlxsw_sp_bridge *bridge,
struct net_device *br_dev)
struct net_device *br_dev,
struct netlink_ext_ack *extack)
{
struct device *dev = bridge->mlxsw_sp->bus_info->dev;
struct mlxsw_sp_bridge_device *bridge_device;
bool vlan_enabled = br_vlan_enabled(br_dev);
int err;
if (vlan_enabled && bridge->vlan_enabled_exists) {
dev_err(dev, "Only one VLAN-aware bridge is supported\n");
NL_SET_ERR_MSG_MOD(extack, "Only one VLAN-aware bridge is supported");
return ERR_PTR(-EINVAL);
}
......@@ -184,13 +232,29 @@ mlxsw_sp_bridge_device_create(struct mlxsw_sp_bridge *bridge,
INIT_LIST_HEAD(&bridge_device->mids_list);
list_add(&bridge_device->list, &bridge->bridges_list);
/* It is possible we already have VXLAN devices enslaved to the bridge.
* In which case, we need to replay their configuration as if they were
* just now enslaved to the bridge.
*/
err = mlxsw_sp_bridge_device_vxlan_init(bridge, br_dev, extack);
if (err)
goto err_vxlan_init;
return bridge_device;
err_vxlan_init:
list_del(&bridge_device->list);
if (bridge_device->vlan_enabled)
bridge->vlan_enabled_exists = false;
kfree(bridge_device);
return ERR_PTR(err);
}
static void
mlxsw_sp_bridge_device_destroy(struct mlxsw_sp_bridge *bridge,
struct mlxsw_sp_bridge_device *bridge_device)
{
mlxsw_sp_bridge_device_vxlan_fini(bridge, bridge_device->dev);
mlxsw_sp_bridge_device_rifs_destroy(bridge->mlxsw_sp,
bridge_device->dev);
list_del(&bridge_device->list);
......@@ -203,7 +267,8 @@ mlxsw_sp_bridge_device_destroy(struct mlxsw_sp_bridge *bridge,
static struct mlxsw_sp_bridge_device *
mlxsw_sp_bridge_device_get(struct mlxsw_sp_bridge *bridge,
struct net_device *br_dev)
struct net_device *br_dev,
struct netlink_ext_ack *extack)
{
struct mlxsw_sp_bridge_device *bridge_device;
......@@ -211,7 +276,7 @@ mlxsw_sp_bridge_device_get(struct mlxsw_sp_bridge *bridge,
if (bridge_device)
return bridge_device;
return mlxsw_sp_bridge_device_create(bridge, br_dev);
return mlxsw_sp_bridge_device_create(bridge, br_dev, extack);
}
static void
......@@ -292,7 +357,8 @@ mlxsw_sp_bridge_port_destroy(struct mlxsw_sp_bridge_port *bridge_port)
static struct mlxsw_sp_bridge_port *
mlxsw_sp_bridge_port_get(struct mlxsw_sp_bridge *bridge,
struct net_device *brport_dev)
struct net_device *brport_dev,
struct netlink_ext_ack *extack)
{
struct net_device *br_dev = netdev_master_upper_dev_get(brport_dev);
struct mlxsw_sp_bridge_device *bridge_device;
......@@ -305,7 +371,7 @@ mlxsw_sp_bridge_port_get(struct mlxsw_sp_bridge *bridge,
return bridge_port;
}
bridge_device = mlxsw_sp_bridge_device_get(bridge, br_dev);
bridge_device = mlxsw_sp_bridge_device_get(bridge, br_dev, extack);
if (IS_ERR(bridge_device))
return ERR_CAST(bridge_device);
......@@ -1000,7 +1066,7 @@ mlxsw_sp_port_vlan_bridge_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
&bridge_vlan->port_vlan_list);
mlxsw_sp_bridge_port_get(mlxsw_sp_port->mlxsw_sp->bridge,
bridge_port->dev);
bridge_port->dev, extack);
mlxsw_sp_port_vlan->bridge_port = bridge_port;
return 0;
......@@ -1990,12 +2056,11 @@ mlxsw_sp_bridge_8021q_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
return err;
}
/* If no other port is member in the VLAN, then the FID does not exist.
* NVE will be enabled on the FID once a port joins the VLAN
*/
fid = mlxsw_sp_fid_8021q_lookup(mlxsw_sp, vid);
if (!fid)
return 0;
fid = mlxsw_sp_fid_8021q_get(mlxsw_sp, vid);
if (IS_ERR(fid)) {
NL_SET_ERR_MSG_MOD(extack, "Failed to create 802.1Q FID");
return PTR_ERR(fid);
}
if (mlxsw_sp_fid_vni_is_set(fid)) {
NL_SET_ERR_MSG_MOD(extack, "VNI is already set on FID");
......@@ -2007,11 +2072,6 @@ mlxsw_sp_bridge_8021q_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
if (err)
goto err_nve_fid_enable;
/* The tunnel port does not hold a reference on the FID. Only
* local ports and the router port
*/
mlxsw_sp_fid_put(fid);
return 0;
err_nve_fid_enable:
......@@ -2048,38 +2108,8 @@ mlxsw_sp_bridge_8021q_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
u16 vid, struct netlink_ext_ack *extack)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
struct net_device *vxlan_dev;
struct mlxsw_sp_fid *fid;
int err;
fid = mlxsw_sp_fid_8021q_get(mlxsw_sp, vid);
if (IS_ERR(fid))
return fid;
if (mlxsw_sp_fid_vni_is_set(fid))
return fid;
/* Find the VxLAN device that has the specified VLAN configured as
* PVID and egress untagged. There can be at most one such device
*/
vxlan_dev = mlxsw_sp_bridge_8021q_vxlan_dev_find(bridge_device->dev,
vid);
if (!vxlan_dev)
return fid;
if (!netif_running(vxlan_dev))
return fid;
err = mlxsw_sp_bridge_8021q_vxlan_join(bridge_device, vxlan_dev, vid,
extack);
if (err)
goto err_vxlan_join;
return fid;
err_vxlan_join:
mlxsw_sp_fid_put(fid);
return ERR_PTR(err);
return mlxsw_sp_fid_8021q_get(mlxsw_sp, vid);
}
static struct mlxsw_sp_fid *
......@@ -2184,9 +2214,9 @@ mlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
struct mlxsw_sp_fid *fid;
int err;
fid = mlxsw_sp_fid_8021d_lookup(mlxsw_sp, bridge_device->dev->ifindex);
if (!fid) {
NL_SET_ERR_MSG_MOD(extack, "Did not find a corresponding FID");
fid = mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device->dev->ifindex);
if (IS_ERR(fid)) {
NL_SET_ERR_MSG_MOD(extack, "Failed to create 802.1D FID");
return -EINVAL;
}
......@@ -2200,11 +2230,6 @@ mlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
if (err)
goto err_nve_fid_enable;
/* The tunnel port does not hold a reference on the FID. Only
* local ports and the router port
*/
mlxsw_sp_fid_put(fid);
return 0;
err_nve_fid_enable:
......@@ -2218,34 +2243,8 @@ mlxsw_sp_bridge_8021d_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
u16 vid, struct netlink_ext_ack *extack)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
struct net_device *vxlan_dev;
struct mlxsw_sp_fid *fid;
int err;
fid = mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device->dev->ifindex);
if (IS_ERR(fid))
return fid;
if (mlxsw_sp_fid_vni_is_set(fid))
return fid;
vxlan_dev = mlxsw_sp_bridge_vxlan_dev_find(bridge_device->dev);
if (!vxlan_dev)
return fid;
if (!netif_running(vxlan_dev))
return fid;
err = mlxsw_sp_bridge_8021d_vxlan_join(bridge_device, vxlan_dev, 0,
extack);
if (err)
goto err_vxlan_join;
return fid;
err_vxlan_join:
mlxsw_sp_fid_put(fid);
return ERR_PTR(err);
return mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device->dev->ifindex);
}
static struct mlxsw_sp_fid *
......@@ -2287,7 +2286,8 @@ int mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_bridge_port *bridge_port;
int err;
bridge_port = mlxsw_sp_bridge_port_get(mlxsw_sp->bridge, brport_dev);
bridge_port = mlxsw_sp_bridge_port_get(mlxsw_sp->bridge, brport_dev,
extack);
if (IS_ERR(bridge_port))
return PTR_ERR(bridge_port);
bridge_device = bridge_port->bridge_device;
......@@ -2351,21 +2351,11 @@ void mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp,
return;
mlxsw_sp_nve_fid_disable(mlxsw_sp, fid);
/* Drop both the reference we just took during lookup and the reference
* the VXLAN device took.
*/
mlxsw_sp_fid_put(fid);
mlxsw_sp_fid_put(fid);
}
struct mlxsw_sp_fid *mlxsw_sp_bridge_fid_get(struct mlxsw_sp *mlxsw_sp,
const struct net_device *br_dev,
u16 vid,
struct netlink_ext_ack *extack)
{
struct mlxsw_sp_bridge_device *bridge_device;
bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
if (WARN_ON(!bridge_device))
return ERR_PTR(-EINVAL);
return bridge_device->ops->fid_get(bridge_device, vid, extack);
}
static void
......
......@@ -8,7 +8,8 @@ lib_dir=$(dirname $0)/../../../net/forwarding
ALL_TESTS="
netdev_pre_up_test
vxlan_vlan_add_test
port_vlan_add_test
vxlan_bridge_create_test
bridge_create_test
"
NUM_NETIFS=2
source $lib_dir/lib.sh
......@@ -106,32 +107,56 @@ vxlan_vlan_add_test()
ip link del dev br1
}
port_vlan_add_test()
vxlan_bridge_create_test()
{
RET=0
ip link add name br1 up type bridge vlan_filtering 1 mcast_snooping 0
# Unsupported configuration: mlxsw demands VXLAN with "noudpcsum".
ip link add name vx1 up type vxlan id 1000 \
local 192.0.2.17 remote 192.0.2.18 \
dstport 4789 tos inherit ttl 100
ip link set dev $swp1 master br1
check_err $?
bridge vlan del dev $swp1 vid 1
# Test with VLAN-aware bridge.
ip link add name br1 up type bridge vlan_filtering 1 mcast_snooping 0
ip link set dev vx1 master br1
ip link set dev $swp1 master br1 2>&1 > /dev/null \
| grep -q mlxsw_spectrum
check_err $?
bridge vlan add dev $swp1 vid 1 pvid untagged 2>&1 >/dev/null \
# Test with VLAN-unaware bridge.
ip link set dev br1 type bridge vlan_filtering 0
ip link set dev $swp1 master br1 2>&1 > /dev/null \
| grep -q mlxsw_spectrum
check_err $?
log_test "extack - map VLAN at port"
log_test "extack - bridge creation with VXLAN"
ip link del dev br1
ip link del dev vx1
}
bridge_create_test()
{
RET=0
ip link add name br1 up type bridge vlan_filtering 1
ip link add name br2 up type bridge vlan_filtering 1
ip link set dev $swp1 master br1
check_err $?
# Only one VLAN-aware bridge is supported, so this should fail with
# an extack.
ip link set dev $swp2 master br2 2>&1 > /dev/null \
| grep -q mlxsw_spectrum
check_err $?
log_test "extack - multiple VLAN-aware bridges creation"
ip link del dev br2
ip link del dev br1
}
......
......@@ -854,20 +854,25 @@ sanitization_vlan_aware_test()
bridge vlan del vid 10 dev vxlan20
bridge vlan add vid 20 dev vxlan20 pvid untagged
# Test that offloading of an unsupported tunnel fails when it is
# triggered by addition of VLAN to a local port
RET=0
# Test that when two VXLAN tunnels with conflicting configurations
# (i.e., different TTL) are enslaved to the same VLAN-aware bridge,
# then the enslavement of a port to the bridge is denied.
# TOS must be set to inherit
ip link set dev vxlan10 type vxlan tos 42
# Use the offload indication of the local route to ensure the VXLAN
# configuration was correctly rollbacked.
ip address add 198.51.100.1/32 dev lo
ip link set dev $swp1 master br0
bridge vlan add vid 10 dev $swp1 &> /dev/null
ip link set dev vxlan10 type vxlan ttl 10
ip link set dev $swp1 master br0 &> /dev/null
check_fail $?
log_test "vlan-aware - failed vlan addition to a local port"
ip route show table local | grep 198.51.100.1 | grep -q offload
check_fail $?
log_test "vlan-aware - failed enslavement to bridge due to conflict"
ip link set dev vxlan10 type vxlan tos inherit
ip link set dev vxlan10 type vxlan ttl 20
ip address del 198.51.100.1/32 dev lo
ip link del dev vxlan20
ip link del dev vxlan10
......@@ -1064,11 +1069,9 @@ offload_indication_vlan_aware_l3vni_test()
ip link set dev vxlan0 master br0
bridge vlan add dev vxlan0 vid 10 pvid untagged
# No local port or router port is member in the VLAN, so tunnel should
# not be offloaded
bridge fdb show brport vxlan0 | grep $zmac | grep self \
| grep -q offload
check_fail $? "vxlan tunnel offloaded when should not"
check_err $? "vxlan tunnel not offloaded when should"
# Configure a VLAN interface and make sure tunnel is offloaded
ip link add link br0 name br10 up type vlan id 10
......
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