Merge branch 'mlxsw-Make-driver-more-robust'

Ido Schimmel says:

====================
mlxsw: Make driver more robust

In recent months we fixed several bugs in the driver that could have
been avoided by re-evaluating some of the involved code paths and by
introducing relevant and comprehensive test cases.

This patchset tries to do that by introducing a set of small and mostly
non-functional changes in addition to a new test. I have further
improvements in mind, but they can be done in a different set.

Patch #1 makes sure we correctly sanitize upper devices of a VLAN
interface.

Patch #2 removes an unexpected behavior from the driver, in which routes
configured on a VLAN interface will cease being offloaded after certain
operations.

Patch #3 is a small cleanup.

Patch #4 simplifies the driver by removing reference counting from VLAN
entries configured on a port.

Patches #5-#6 simplify linking/unlinking from a bridge, especially when
LAG and VLAN devices are involved. They make both operations symmetric
even when ports are unlinked from a bridged LAG device.

Patch #7-#9 make router interface (RIF) deletion more robust by removing
reliance on device chain to indicate whether a NETDEV_DOWN event in the
inet{,6}addr notification chains should be processed. This is due to the
fact that IP addresses can be flushed from a netdev after it was
unlinked from its lower device.

Patch #10 adds a new test to for valid and invalid configurations over
mlxsw ports. Some of the test cases are derived from recent fixes. I
expect that more test cases will be added over time.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2018-12-19 12:28:08 -08:00
commit 4ab0edecaf
6 changed files with 640 additions and 184 deletions

View file

@ -1141,16 +1141,20 @@ static void mlxsw_sp_port_vlan_flush(struct mlxsw_sp_port *mlxsw_sp_port)
list_for_each_entry_safe(mlxsw_sp_port_vlan, tmp,
&mlxsw_sp_port->vlans_list, list)
mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan);
mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
}
static struct mlxsw_sp_port_vlan *
struct mlxsw_sp_port_vlan *
mlxsw_sp_port_vlan_create(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
{
struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
bool untagged = vid == 1;
int err;
mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
if (mlxsw_sp_port_vlan)
return ERR_PTR(-EEXIST);
err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true, untagged);
if (err)
return ERR_PTR(err);
@ -1162,7 +1166,6 @@ mlxsw_sp_port_vlan_create(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
}
mlxsw_sp_port_vlan->mlxsw_sp_port = mlxsw_sp_port;
mlxsw_sp_port_vlan->ref_count = 1;
mlxsw_sp_port_vlan->vid = vid;
list_add(&mlxsw_sp_port_vlan->list, &mlxsw_sp_port->vlans_list);
@ -1173,46 +1176,21 @@ mlxsw_sp_port_vlan_create(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
return ERR_PTR(err);
}
static void
mlxsw_sp_port_vlan_destroy(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
void mlxsw_sp_port_vlan_destroy(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
{
struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
u16 vid = mlxsw_sp_port_vlan->vid;
if (mlxsw_sp_port_vlan->bridge_port)
mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan);
else if (mlxsw_sp_port_vlan->fid)
mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
list_del(&mlxsw_sp_port_vlan->list);
kfree(mlxsw_sp_port_vlan);
mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
}
struct mlxsw_sp_port_vlan *
mlxsw_sp_port_vlan_get(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
{
struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
if (mlxsw_sp_port_vlan) {
mlxsw_sp_port_vlan->ref_count++;
return mlxsw_sp_port_vlan;
}
return mlxsw_sp_port_vlan_create(mlxsw_sp_port, vid);
}
void mlxsw_sp_port_vlan_put(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
{
struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
if (--mlxsw_sp_port_vlan->ref_count != 0)
return;
if (mlxsw_sp_port_vlan->bridge_port)
mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan);
else if (fid)
mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
}
static int mlxsw_sp_port_add_vid(struct net_device *dev,
__be16 __always_unused proto, u16 vid)
{
@ -1224,7 +1202,7 @@ static int mlxsw_sp_port_add_vid(struct net_device *dev,
if (!vid)
return 0;
return PTR_ERR_OR_ZERO(mlxsw_sp_port_vlan_get(mlxsw_sp_port, vid));
return PTR_ERR_OR_ZERO(mlxsw_sp_port_vlan_create(mlxsw_sp_port, vid));
}
static int mlxsw_sp_port_kill_vid(struct net_device *dev,
@ -1242,7 +1220,7 @@ static int mlxsw_sp_port_kill_vid(struct net_device *dev,
mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
if (!mlxsw_sp_port_vlan)
return 0;
mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan);
mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
return 0;
}
@ -3198,12 +3176,12 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
goto err_port_nve_init;
}
mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_get(mlxsw_sp_port, 1);
mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_create(mlxsw_sp_port, 1);
if (IS_ERR(mlxsw_sp_port_vlan)) {
dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to create VID 1\n",
mlxsw_sp_port->local_port);
err = PTR_ERR(mlxsw_sp_port_vlan);
goto err_port_vlan_get;
goto err_port_vlan_create;
}
mlxsw_sp_port_switchdev_init(mlxsw_sp_port);
@ -3224,8 +3202,8 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
err_register_netdev:
mlxsw_sp->ports[local_port] = NULL;
mlxsw_sp_port_switchdev_fini(mlxsw_sp_port);
mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan);
err_port_vlan_get:
mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
err_port_vlan_create:
mlxsw_sp_port_nve_fini(mlxsw_sp_port);
err_port_nve_init:
mlxsw_sp_tc_qdisc_fini(mlxsw_sp_port);
@ -4520,6 +4498,25 @@ void mlxsw_sp_port_dev_put(struct mlxsw_sp_port *mlxsw_sp_port)
dev_put(mlxsw_sp_port->dev);
}
static void
mlxsw_sp_port_lag_uppers_cleanup(struct mlxsw_sp_port *mlxsw_sp_port,
struct net_device *lag_dev)
{
struct net_device *br_dev = netdev_master_upper_dev_get(lag_dev);
struct net_device *upper_dev;
struct list_head *iter;
if (netif_is_bridge_port(lag_dev))
mlxsw_sp_port_bridge_leave(mlxsw_sp_port, lag_dev, br_dev);
netdev_for_each_upper_dev_rcu(lag_dev, upper_dev, iter) {
if (!netif_is_bridge_port(upper_dev))
continue;
br_dev = netdev_master_upper_dev_get(upper_dev);
mlxsw_sp_port_bridge_leave(mlxsw_sp_port, upper_dev, br_dev);
}
}
static int mlxsw_sp_lag_create(struct mlxsw_sp *mlxsw_sp, u16 lag_id)
{
char sldr_pl[MLXSW_REG_SLDR_LEN];
@ -4712,6 +4709,10 @@ static void mlxsw_sp_port_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port,
/* Any VLANs configured on the port are no longer valid */
mlxsw_sp_port_vlan_flush(mlxsw_sp_port);
/* Make the LAG and its directly linked uppers leave bridges they
* are memeber in
*/
mlxsw_sp_port_lag_uppers_cleanup(mlxsw_sp_port, lag_dev);
if (lag->ref_count == 1)
mlxsw_sp_lag_destroy(mlxsw_sp, lag_id);
@ -4721,7 +4722,7 @@ static void mlxsw_sp_port_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port,
mlxsw_sp_port->lagged = 0;
lag->ref_count--;
mlxsw_sp_port_vlan_get(mlxsw_sp_port, 1);
mlxsw_sp_port_vlan_create(mlxsw_sp_port, 1);
/* Make sure untagged frames are allowed to ingress */
mlxsw_sp_port_pvid_set(mlxsw_sp_port, 1);
}
@ -5000,6 +5001,16 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
} else if (netif_is_macvlan(upper_dev)) {
if (!info->linking)
mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
} else if (is_vlan_dev(upper_dev)) {
struct net_device *br_dev;
if (!netif_is_bridge_port(upper_dev))
break;
if (info->linking)
break;
br_dev = netdev_master_upper_dev_get(upper_dev);
mlxsw_sp_port_bridge_leave(mlxsw_sp_port, upper_dev,
br_dev);
}
break;
}
@ -5156,6 +5167,48 @@ static int mlxsw_sp_netdevice_lag_port_vlan_event(struct net_device *vlan_dev,
return 0;
}
static int mlxsw_sp_netdevice_bridge_vlan_event(struct net_device *vlan_dev,
struct net_device *br_dev,
unsigned long event, void *ptr,
u16 vid)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(vlan_dev);
struct netdev_notifier_changeupper_info *info = ptr;
struct netlink_ext_ack *extack;
struct net_device *upper_dev;
if (!mlxsw_sp)
return 0;
extack = netdev_notifier_info_to_extack(&info->info);
switch (event) {
case NETDEV_PRECHANGEUPPER:
upper_dev = info->upper_dev;
if (!netif_is_macvlan(upper_dev)) {
NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
return -EOPNOTSUPP;
}
if (!info->linking)
break;
if (netif_is_macvlan(upper_dev) &&
!mlxsw_sp_rif_find_by_dev(mlxsw_sp, vlan_dev)) {
NL_SET_ERR_MSG_MOD(extack, "macvlan is only supported on top of router interfaces");
return -EOPNOTSUPP;
}
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;
}
return 0;
}
static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev,
unsigned long event, void *ptr)
{
@ -5169,6 +5222,9 @@ static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev,
return mlxsw_sp_netdevice_lag_port_vlan_event(vlan_dev,
real_dev, event,
ptr, vid);
else if (netif_is_bridge_master(real_dev))
return mlxsw_sp_netdevice_bridge_vlan_event(vlan_dev, real_dev,
event, ptr, vid);
return 0;
}
@ -5358,18 +5414,10 @@ static struct notifier_block mlxsw_sp_inetaddr_valid_nb __read_mostly = {
.notifier_call = mlxsw_sp_inetaddr_valid_event,
};
static struct notifier_block mlxsw_sp_inetaddr_nb __read_mostly = {
.notifier_call = mlxsw_sp_inetaddr_event,
};
static struct notifier_block mlxsw_sp_inet6addr_valid_nb __read_mostly = {
.notifier_call = mlxsw_sp_inet6addr_valid_event,
};
static struct notifier_block mlxsw_sp_inet6addr_nb __read_mostly = {
.notifier_call = mlxsw_sp_inet6addr_event,
};
static const struct pci_device_id mlxsw_sp1_pci_id_table[] = {
{PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM), 0},
{0, },
@ -5395,9 +5443,7 @@ static int __init mlxsw_sp_module_init(void)
int err;
register_inetaddr_validator_notifier(&mlxsw_sp_inetaddr_valid_nb);
register_inetaddr_notifier(&mlxsw_sp_inetaddr_nb);
register_inet6addr_validator_notifier(&mlxsw_sp_inet6addr_valid_nb);
register_inet6addr_notifier(&mlxsw_sp_inet6addr_nb);
err = mlxsw_core_driver_register(&mlxsw_sp1_driver);
if (err)
@ -5424,9 +5470,7 @@ static int __init mlxsw_sp_module_init(void)
err_sp2_core_driver_register:
mlxsw_core_driver_unregister(&mlxsw_sp1_driver);
err_sp1_core_driver_register:
unregister_inet6addr_notifier(&mlxsw_sp_inet6addr_nb);
unregister_inet6addr_validator_notifier(&mlxsw_sp_inet6addr_valid_nb);
unregister_inetaddr_notifier(&mlxsw_sp_inetaddr_nb);
unregister_inetaddr_validator_notifier(&mlxsw_sp_inetaddr_valid_nb);
return err;
}
@ -5437,9 +5481,7 @@ static void __exit mlxsw_sp_module_exit(void)
mlxsw_pci_driver_unregister(&mlxsw_sp1_pci_driver);
mlxsw_core_driver_unregister(&mlxsw_sp2_driver);
mlxsw_core_driver_unregister(&mlxsw_sp1_driver);
unregister_inet6addr_notifier(&mlxsw_sp_inet6addr_nb);
unregister_inet6addr_validator_notifier(&mlxsw_sp_inet6addr_valid_nb);
unregister_inetaddr_notifier(&mlxsw_sp_inetaddr_nb);
unregister_inetaddr_validator_notifier(&mlxsw_sp_inetaddr_valid_nb);
}

View file

@ -190,7 +190,6 @@ struct mlxsw_sp_port_vlan {
struct list_head list;
struct mlxsw_sp_port *mlxsw_sp_port;
struct mlxsw_sp_fid *fid;
unsigned int ref_count;
u16 vid;
struct mlxsw_sp_bridge_port *bridge_port;
struct list_head bridge_vlan_node;
@ -410,8 +409,8 @@ int mlxsw_sp_port_vid_learning_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid,
bool learn_enable);
int mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid);
struct mlxsw_sp_port_vlan *
mlxsw_sp_port_vlan_get(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid);
void mlxsw_sp_port_vlan_put(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan);
mlxsw_sp_port_vlan_create(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid);
void mlxsw_sp_port_vlan_destroy(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan);
int mlxsw_sp_port_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin,
u16 vid_end, bool is_member, bool untagged);
int mlxsw_sp_flow_counter_get(struct mlxsw_sp *mlxsw_sp,
@ -459,12 +458,8 @@ int mlxsw_sp_netdevice_router_port_event(struct net_device *dev,
unsigned long event, void *ptr);
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,
unsigned long event, void *ptr);
int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
unsigned long event, void *ptr);
int mlxsw_sp_inet6addr_valid_event(struct notifier_block *unused,
unsigned long event, void *ptr);
int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
@ -484,7 +479,6 @@ mlxsw_sp_netdevice_ipip_ul_event(struct mlxsw_sp *mlxsw_sp,
struct netdev_notifier_info *info);
void
mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan);
void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif);
void mlxsw_sp_rif_destroy_by_dev(struct mlxsw_sp *mlxsw_sp,
struct net_device *dev);
struct mlxsw_sp_rif *mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
@ -785,10 +779,10 @@ int mlxsw_sp_fid_port_vid_map(struct mlxsw_sp_fid *fid,
struct mlxsw_sp_port *mlxsw_sp_port, u16 vid);
void mlxsw_sp_fid_port_vid_unmap(struct mlxsw_sp_fid *fid,
struct mlxsw_sp_port *mlxsw_sp_port, u16 vid);
enum mlxsw_sp_rif_type mlxsw_sp_fid_rif_type(const struct mlxsw_sp_fid *fid);
u16 mlxsw_sp_fid_index(const struct mlxsw_sp_fid *fid);
enum mlxsw_sp_fid_type mlxsw_sp_fid_type(const struct mlxsw_sp_fid *fid);
void mlxsw_sp_fid_rif_set(struct mlxsw_sp_fid *fid, struct mlxsw_sp_rif *rif);
struct mlxsw_sp_rif *mlxsw_sp_fid_rif(const struct mlxsw_sp_fid *fid);
enum mlxsw_sp_rif_type
mlxsw_sp_fid_type_rif_type(const struct mlxsw_sp *mlxsw_sp,
enum mlxsw_sp_fid_type type);

View file

@ -349,11 +349,6 @@ void mlxsw_sp_fid_port_vid_unmap(struct mlxsw_sp_fid *fid,
fid->fid_family->ops->port_vid_unmap(fid, mlxsw_sp_port, vid);
}
enum mlxsw_sp_rif_type mlxsw_sp_fid_rif_type(const struct mlxsw_sp_fid *fid)
{
return fid->fid_family->rif_type;
}
u16 mlxsw_sp_fid_index(const struct mlxsw_sp_fid *fid)
{
return fid->fid_index;
@ -369,6 +364,11 @@ void mlxsw_sp_fid_rif_set(struct mlxsw_sp_fid *fid, struct mlxsw_sp_rif *rif)
fid->rif = rif;
}
struct mlxsw_sp_rif *mlxsw_sp_fid_rif(const struct mlxsw_sp_fid *fid)
{
return fid->rif;
}
enum mlxsw_sp_rif_type
mlxsw_sp_fid_type_rif_type(const struct mlxsw_sp *mlxsw_sp,
enum mlxsw_sp_fid_type type)
@ -1083,20 +1083,16 @@ 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 == 1 && fid->rif) {
/* Destroy the associated RIF and let it drop the last
* reference on the FID.
*/
return mlxsw_sp_rif_destroy(fid->rif);
} else if (fid->ref_count == 0) {
list_del(&fid->list);
rhashtable_remove_fast(&mlxsw_sp->fid_core->fid_ht,
&fid->ht_node, mlxsw_sp_fid_ht_params);
fid->fid_family->ops->deconfigure(fid);
__clear_bit(fid->fid_index - fid_family->start_index,
fid_family->fids_bitmap);
kfree(fid);
}
if (--fid->ref_count != 0)
return;
list_del(&fid->list);
rhashtable_remove_fast(&mlxsw_sp->fid_core->fid_ht,
&fid->ht_node, mlxsw_sp_fid_ht_params);
fid->fid_family->ops->deconfigure(fid);
__clear_bit(fid->fid_index - fid_family->start_index,
fid_family->fids_bitmap);
kfree(fid);
}
struct mlxsw_sp_fid *mlxsw_sp_fid_8021q_get(struct mlxsw_sp *mlxsw_sp, u16 vid)

View file

@ -15,6 +15,7 @@
#include <linux/gcd.h>
#include <linux/random.h>
#include <linux/if_macvlan.h>
#include <linux/refcount.h>
#include <net/netevent.h>
#include <net/neighbour.h>
#include <net/arp.h>
@ -70,6 +71,8 @@ struct mlxsw_sp_router {
bool aborted;
struct notifier_block fib_nb;
struct notifier_block netevent_nb;
struct notifier_block inetaddr_nb;
struct notifier_block inet6addr_nb;
const struct mlxsw_sp_rif_ops **rif_ops_arr;
const struct mlxsw_sp_ipip_ops **ipip_ops_arr;
};
@ -104,6 +107,7 @@ struct mlxsw_sp_rif_params {
struct mlxsw_sp_rif_subport {
struct mlxsw_sp_rif common;
refcount_t ref_count;
union {
u16 system_port;
u16 lag_id;
@ -136,6 +140,7 @@ struct mlxsw_sp_rif_ops {
void (*fdb_del)(struct mlxsw_sp_rif *rif, const char *mac);
};
static void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif);
static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree);
static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_lpm_tree *lpm_tree);
@ -6297,6 +6302,7 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
err = -ENOMEM;
goto err_rif_alloc;
}
dev_hold(rif->dev);
rif->mlxsw_sp = mlxsw_sp;
rif->ops = ops;
@ -6335,6 +6341,7 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
if (fid)
mlxsw_sp_fid_put(fid);
err_fid_get:
dev_put(rif->dev);
kfree(rif);
err_rif_alloc:
err_rif_index_alloc:
@ -6343,7 +6350,7 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
return ERR_PTR(err);
}
void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
static void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
{
const struct mlxsw_sp_rif_ops *ops = rif->ops;
struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
@ -6362,6 +6369,7 @@ void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
if (fid)
/* Loopback RIFs are not associated with a FID. */
mlxsw_sp_fid_put(fid);
dev_put(rif->dev);
kfree(rif);
vr->rif_count--;
mlxsw_sp_vr_put(mlxsw_sp, vr);
@ -6392,6 +6400,40 @@ mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
params->system_port = mlxsw_sp_port->local_port;
}
static struct mlxsw_sp_rif_subport *
mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
{
return container_of(rif, struct mlxsw_sp_rif_subport, common);
}
static struct mlxsw_sp_rif *
mlxsw_sp_rif_subport_get(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_rif_params *params,
struct netlink_ext_ack *extack)
{
struct mlxsw_sp_rif_subport *rif_subport;
struct mlxsw_sp_rif *rif;
rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, params->dev);
if (!rif)
return mlxsw_sp_rif_create(mlxsw_sp, params, extack);
rif_subport = mlxsw_sp_rif_subport_rif(rif);
refcount_inc(&rif_subport->ref_count);
return rif;
}
static void mlxsw_sp_rif_subport_put(struct mlxsw_sp_rif *rif)
{
struct mlxsw_sp_rif_subport *rif_subport;
rif_subport = mlxsw_sp_rif_subport_rif(rif);
if (!refcount_dec_and_test(&rif_subport->ref_count))
return;
mlxsw_sp_rif_destroy(rif);
}
static int
mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
struct net_device *l3_dev,
@ -6399,22 +6441,18 @@ mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
{
struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
struct mlxsw_sp_rif_params params = {
.dev = l3_dev,
};
u16 vid = mlxsw_sp_port_vlan->vid;
struct mlxsw_sp_rif *rif;
struct mlxsw_sp_fid *fid;
int err;
rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
if (!rif) {
struct mlxsw_sp_rif_params params = {
.dev = l3_dev,
};
mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
rif = mlxsw_sp_rif_create(mlxsw_sp, &params, extack);
if (IS_ERR(rif))
return PTR_ERR(rif);
}
mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
rif = mlxsw_sp_rif_subport_get(mlxsw_sp, &params, extack);
if (IS_ERR(rif))
return PTR_ERR(rif);
/* FID was already created, just take a reference */
fid = rif->ops->fid_get(rif, extack);
@ -6441,6 +6479,7 @@ mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
err_fid_port_vid_map:
mlxsw_sp_fid_put(fid);
mlxsw_sp_rif_subport_put(rif);
return err;
}
@ -6449,6 +6488,7 @@ mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
{
struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
struct mlxsw_sp_rif *rif = mlxsw_sp_fid_rif(fid);
u16 vid = mlxsw_sp_port_vlan->vid;
if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
@ -6458,10 +6498,8 @@ mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
/* If router port holds the last reference on the rFID, then the
* associated Sub-port RIF will be destroyed.
*/
mlxsw_sp_fid_put(fid);
mlxsw_sp_rif_subport_put(rif);
}
static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
@ -6535,11 +6573,11 @@ static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
extack);
}
static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
static int mlxsw_sp_inetaddr_bridge_event(struct mlxsw_sp *mlxsw_sp,
struct net_device *l3_dev,
unsigned long event,
struct netlink_ext_ack *extack)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
struct mlxsw_sp_rif_params params = {
.dev = l3_dev,
};
@ -6560,7 +6598,8 @@ static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
return 0;
}
static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
static int mlxsw_sp_inetaddr_vlan_event(struct mlxsw_sp *mlxsw_sp,
struct net_device *vlan_dev,
unsigned long event,
struct netlink_ext_ack *extack)
{
@ -6577,7 +6616,8 @@ static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
vid, extack);
else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event, extack);
return mlxsw_sp_inetaddr_bridge_event(mlxsw_sp, vlan_dev, event,
extack);
return 0;
}
@ -6678,16 +6718,11 @@ void mlxsw_sp_rif_macvlan_del(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp_fid_index(rif->fid), false);
}
static int mlxsw_sp_inetaddr_macvlan_event(struct net_device *macvlan_dev,
static int mlxsw_sp_inetaddr_macvlan_event(struct mlxsw_sp *mlxsw_sp,
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);
@ -6726,7 +6761,8 @@ static int mlxsw_sp_router_port_check_rif_addr(struct mlxsw_sp *mlxsw_sp,
return 0;
}
static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
static int __mlxsw_sp_inetaddr_event(struct mlxsw_sp *mlxsw_sp,
struct net_device *dev,
unsigned long event,
struct netlink_ext_ack *extack)
{
@ -6735,21 +6771,24 @@ static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
else if (netif_is_lag_master(dev))
return mlxsw_sp_inetaddr_lag_event(dev, event, extack);
else if (netif_is_bridge_master(dev))
return mlxsw_sp_inetaddr_bridge_event(dev, event, extack);
return mlxsw_sp_inetaddr_bridge_event(mlxsw_sp, dev, event,
extack);
else if (is_vlan_dev(dev))
return mlxsw_sp_inetaddr_vlan_event(dev, event, extack);
return mlxsw_sp_inetaddr_vlan_event(mlxsw_sp, dev, event,
extack);
else if (netif_is_macvlan(dev))
return mlxsw_sp_inetaddr_macvlan_event(dev, event, extack);
return mlxsw_sp_inetaddr_macvlan_event(mlxsw_sp, dev, event,
extack);
else
return 0;
}
int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
unsigned long event, void *ptr)
static int mlxsw_sp_inetaddr_event(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
struct net_device *dev = ifa->ifa_dev->dev;
struct mlxsw_sp *mlxsw_sp;
struct mlxsw_sp_router *router;
struct mlxsw_sp_rif *rif;
int err = 0;
@ -6757,15 +6796,12 @@ int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
if (event == NETDEV_UP)
goto out;
mlxsw_sp = mlxsw_sp_lower_get(dev);
if (!mlxsw_sp)
goto out;
rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
router = container_of(nb, struct mlxsw_sp_router, inetaddr_nb);
rif = mlxsw_sp_rif_find_by_dev(router->mlxsw_sp, dev);
if (!mlxsw_sp_rif_should_config(rif, dev, event))
goto out;
err = __mlxsw_sp_inetaddr_event(dev, event, NULL);
err = __mlxsw_sp_inetaddr_event(router->mlxsw_sp, dev, event, NULL);
out:
return notifier_from_errno(err);
}
@ -6792,13 +6828,14 @@ int mlxsw_sp_inetaddr_valid_event(struct notifier_block *unused,
if (err)
goto out;
err = __mlxsw_sp_inetaddr_event(dev, event, ivi->extack);
err = __mlxsw_sp_inetaddr_event(mlxsw_sp, dev, event, ivi->extack);
out:
return notifier_from_errno(err);
}
struct mlxsw_sp_inet6addr_event_work {
struct work_struct work;
struct mlxsw_sp *mlxsw_sp;
struct net_device *dev;
unsigned long event;
};
@ -6807,21 +6844,18 @@ static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
{
struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
struct mlxsw_sp *mlxsw_sp = inet6addr_work->mlxsw_sp;
struct net_device *dev = inet6addr_work->dev;
unsigned long event = inet6addr_work->event;
struct mlxsw_sp *mlxsw_sp;
struct mlxsw_sp_rif *rif;
rtnl_lock();
mlxsw_sp = mlxsw_sp_lower_get(dev);
if (!mlxsw_sp)
goto out;
rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
if (!mlxsw_sp_rif_should_config(rif, dev, event))
goto out;
__mlxsw_sp_inetaddr_event(dev, event, NULL);
__mlxsw_sp_inetaddr_event(mlxsw_sp, dev, event, NULL);
out:
rtnl_unlock();
dev_put(dev);
@ -6829,25 +6863,25 @@ static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
}
/* Called with rcu_read_lock() */
int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
unsigned long event, void *ptr)
static int mlxsw_sp_inet6addr_event(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
struct net_device *dev = if6->idev->dev;
struct mlxsw_sp_router *router;
/* NETDEV_UP event is handled by mlxsw_sp_inet6addr_valid_event */
if (event == NETDEV_UP)
return NOTIFY_DONE;
if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
return NOTIFY_DONE;
inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
if (!inet6addr_work)
return NOTIFY_BAD;
router = container_of(nb, struct mlxsw_sp_router, inet6addr_nb);
INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
inet6addr_work->mlxsw_sp = router->mlxsw_sp;
inet6addr_work->dev = dev;
inet6addr_work->event = event;
dev_hold(dev);
@ -6878,7 +6912,7 @@ int mlxsw_sp_inet6addr_valid_event(struct notifier_block *unused,
if (err)
goto out;
err = __mlxsw_sp_inetaddr_event(dev, event, i6vi->extack);
err = __mlxsw_sp_inetaddr_event(mlxsw_sp, dev, event, i6vi->extack);
out:
return notifier_from_errno(err);
}
@ -6997,9 +7031,10 @@ static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
*/
rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
if (rif)
__mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN, extack);
__mlxsw_sp_inetaddr_event(mlxsw_sp, l3_dev, NETDEV_DOWN,
extack);
return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP, extack);
return __mlxsw_sp_inetaddr_event(mlxsw_sp, l3_dev, NETDEV_UP, extack);
}
static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
@ -7010,7 +7045,7 @@ static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
if (!rif)
return;
__mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN, NULL);
__mlxsw_sp_inetaddr_event(mlxsw_sp, l3_dev, NETDEV_DOWN, NULL);
}
int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
@ -7064,18 +7099,13 @@ static int mlxsw_sp_rif_macvlan_flush(struct mlxsw_sp_rif *rif)
__mlxsw_sp_rif_macvlan_flush, rif);
}
static struct mlxsw_sp_rif_subport *
mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
{
return container_of(rif, struct mlxsw_sp_rif_subport, common);
}
static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
const struct mlxsw_sp_rif_params *params)
{
struct mlxsw_sp_rif_subport *rif_subport;
rif_subport = mlxsw_sp_rif_subport_rif(rif);
refcount_set(&rif_subport->ref_count, 1);
rif_subport->vid = params->vid;
rif_subport->lag = params->lag;
if (params->lag)
@ -7627,6 +7657,16 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
mlxsw_sp->router = router;
router->mlxsw_sp = mlxsw_sp;
router->inetaddr_nb.notifier_call = mlxsw_sp_inetaddr_event;
err = register_inetaddr_notifier(&router->inetaddr_nb);
if (err)
goto err_register_inetaddr_notifier;
router->inet6addr_nb.notifier_call = mlxsw_sp_inet6addr_event;
err = register_inet6addr_notifier(&router->inet6addr_nb);
if (err)
goto err_register_inet6addr_notifier;
INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
err = __mlxsw_sp_router_init(mlxsw_sp);
if (err)
@ -7712,6 +7752,10 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
err_rifs_init:
__mlxsw_sp_router_fini(mlxsw_sp);
err_router_init:
unregister_inet6addr_notifier(&router->inet6addr_nb);
err_register_inet6addr_notifier:
unregister_inetaddr_notifier(&router->inetaddr_nb);
err_register_inetaddr_notifier:
kfree(mlxsw_sp->router);
return err;
}
@ -7729,5 +7773,7 @@ void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
mlxsw_sp_ipips_fini(mlxsw_sp);
mlxsw_sp_rifs_fini(mlxsw_sp);
__mlxsw_sp_router_fini(mlxsw_sp);
unregister_inet6addr_notifier(&mlxsw_sp->router->inet6addr_nb);
unregister_inetaddr_notifier(&mlxsw_sp->router->inetaddr_nb);
kfree(mlxsw_sp->router);
}

View file

@ -290,30 +290,6 @@ mlxsw_sp_bridge_port_destroy(struct mlxsw_sp_bridge_port *bridge_port)
kfree(bridge_port);
}
static bool
mlxsw_sp_bridge_port_should_destroy(const struct mlxsw_sp_bridge_port *
bridge_port)
{
struct net_device *dev = bridge_port->dev;
struct mlxsw_sp *mlxsw_sp;
if (is_vlan_dev(dev))
mlxsw_sp = mlxsw_sp_lower_get(vlan_dev_real_dev(dev));
else
mlxsw_sp = mlxsw_sp_lower_get(dev);
/* In case ports were pulled from out of a bridged LAG, then
* it's possible the reference count isn't zero, yet the bridge
* port should be destroyed, as it's no longer an upper of ours.
*/
if (!mlxsw_sp && list_empty(&bridge_port->vlans_list))
return true;
else if (bridge_port->ref_count == 0)
return true;
else
return false;
}
static struct mlxsw_sp_bridge_port *
mlxsw_sp_bridge_port_get(struct mlxsw_sp_bridge *bridge,
struct net_device *brport_dev)
@ -351,8 +327,7 @@ static void mlxsw_sp_bridge_port_put(struct mlxsw_sp_bridge *bridge,
{
struct mlxsw_sp_bridge_device *bridge_device;
bridge_port->ref_count--;
if (!mlxsw_sp_bridge_port_should_destroy(bridge_port))
if (--bridge_port->ref_count != 0)
return;
bridge_device = bridge_port->bridge_device;
mlxsw_sp_bridge_port_destroy(bridge_port);
@ -1021,10 +996,8 @@ mlxsw_sp_port_vlan_bridge_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
int err;
/* No need to continue if only VLAN flags were changed */
if (mlxsw_sp_port_vlan->bridge_port) {
mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan);
if (mlxsw_sp_port_vlan->bridge_port)
return 0;
}
err = mlxsw_sp_port_vlan_fid_join(mlxsw_sp_port_vlan, bridge_port,
extack);
@ -1105,16 +1078,32 @@ static int
mlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_bridge_port *bridge_port,
u16 vid, bool is_untagged, bool is_pvid,
struct netlink_ext_ack *extack)
struct netlink_ext_ack *extack,
struct switchdev_trans *trans)
{
u16 pvid = mlxsw_sp_port_pvid_determine(mlxsw_sp_port, vid, is_pvid);
struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
u16 old_pvid = mlxsw_sp_port->pvid;
int err;
mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_get(mlxsw_sp_port, vid);
if (IS_ERR(mlxsw_sp_port_vlan))
return PTR_ERR(mlxsw_sp_port_vlan);
/* The only valid scenario in which a port-vlan already exists, is if
* the VLAN flags were changed and the port-vlan is associated with the
* correct bridge port
*/
mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
if (mlxsw_sp_port_vlan &&
mlxsw_sp_port_vlan->bridge_port != bridge_port)
return -EEXIST;
if (switchdev_trans_ph_prepare(trans))
return 0;
if (!mlxsw_sp_port_vlan) {
mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_create(mlxsw_sp_port,
vid);
if (IS_ERR(mlxsw_sp_port_vlan))
return PTR_ERR(mlxsw_sp_port_vlan);
}
err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true,
is_untagged);
@ -1137,7 +1126,7 @@ mlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port,
err_port_pvid_set:
mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
err_port_vlan_set:
mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan);
mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
return err;
}
@ -1199,9 +1188,6 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
return err;
}
if (switchdev_trans_ph_prepare(trans))
return 0;
bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
if (WARN_ON(!bridge_port))
return -EINVAL;
@ -1214,7 +1200,7 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
err = mlxsw_sp_bridge_port_vlan_add(mlxsw_sp_port, bridge_port,
vid, flag_untagged,
flag_pvid, extack);
flag_pvid, extack, trans);
if (err)
return err;
}
@ -1832,7 +1818,7 @@ mlxsw_sp_bridge_port_vlan_del(struct mlxsw_sp_port *mlxsw_sp_port,
mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan);
mlxsw_sp_port_pvid_set(mlxsw_sp_port, pvid);
mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan);
mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
}
static int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
@ -2000,7 +1986,7 @@ mlxsw_sp_bridge_8021q_port_join(struct mlxsw_sp_bridge_device *bridge_device,
return -EINVAL;
/* Let VLAN-aware bridge take care of its own VLANs */
mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan);
mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
return 0;
}
@ -2010,7 +1996,7 @@ mlxsw_sp_bridge_8021q_port_leave(struct mlxsw_sp_bridge_device *bridge_device,
struct mlxsw_sp_bridge_port *bridge_port,
struct mlxsw_sp_port *mlxsw_sp_port)
{
mlxsw_sp_port_vlan_get(mlxsw_sp_port, 1);
mlxsw_sp_port_vlan_create(mlxsw_sp_port, 1);
/* Make sure untagged frames are allowed to ingress */
mlxsw_sp_port_pvid_set(mlxsw_sp_port, 1);
}

View file

@ -13,9 +13,23 @@ ALL_TESTS="
rif_set_addr_test
rif_inherit_bridge_addr_test
rif_non_inherit_bridge_addr_test
vlan_interface_deletion_test
bridge_deletion_test
bridge_vlan_flags_test
vlan_1_test
lag_bridge_upper_test
duplicate_vlans_test
vlan_rif_refcount_test
subport_rif_refcount_test
vlan_dev_deletion_test
lag_unlink_slaves_test
lag_dev_deletion_test
vlan_interface_uppers_test
devlink_reload_test
"
NUM_NETIFS=2
source $lib_dir/lib.sh
source $lib_dir/devlink_lib.sh
setup_prepare()
{
@ -160,6 +174,384 @@ rif_non_inherit_bridge_addr_test()
ip addr del dev $swp1 192.0.2.1/28
}
vlan_interface_deletion_test()
{
# Test that when a VLAN interface is deleted, its associated router
# interface (RIF) is correctly deleted and not leaked. See commit
# c360867ec46a ("mlxsw: spectrum: Delete RIF when VLAN device is
# removed") for more details
RET=0
ip link add name br0 type bridge vlan_filtering 1
ip link set dev $swp1 master br0
ip link add link br0 name br0.10 type vlan id 10
ip -6 address add 2001:db8:1::1/64 dev br0.10
ip link del dev br0.10
# If we leaked the previous RIF, then this should produce a trace
ip link add link br0 name br0.20 type vlan id 20
ip -6 address add 2001:db8:1::1/64 dev br0.20
ip link del dev br0.20
log_test "vlan interface deletion"
ip link del dev br0
}
bridge_deletion_test()
{
# Test that when a bridge with VLAN interfaces is deleted, we correctly
# delete the associated RIFs. See commit 602b74eda813 ("mlxsw:
# spectrum_switchdev: Do not leak RIFs when removing bridge") for more
# details
RET=0
ip link add name br0 type bridge vlan_filtering 1
ip link set dev $swp1 master br0
ip -6 address add 2001:db8::1/64 dev br0
ip link add link br0 name br0.10 type vlan id 10
ip -6 address add 2001:db8:1::1/64 dev br0.10
ip link add link br0 name br0.20 type vlan id 20
ip -6 address add 2001:db8:2::1/64 dev br0.20
ip link del dev br0
# If we leaked previous RIFs, then this should produce a trace
ip -6 address add 2001:db8:1::1/64 dev $swp1
ip -6 address del 2001:db8:1::1/64 dev $swp1
log_test "bridge deletion"
}
bridge_vlan_flags_test()
{
# Test that when bridge VLAN flags are toggled, we do not take
# unnecessary references on related structs. See commit 9e25826ffc94
# ("mlxsw: spectrum_switchdev: Fix port_vlan refcounting") for more
# details
RET=0
ip link add name br0 type bridge vlan_filtering 1
ip link set dev $swp1 master br0
bridge vlan add vid 10 dev $swp1 pvid untagged
bridge vlan add vid 10 dev $swp1 untagged
bridge vlan add vid 10 dev $swp1 pvid
bridge vlan add vid 10 dev $swp1
ip link del dev br0
# If we did not handle references correctly, then this should produce a
# trace
devlink dev reload "$DEVLINK_DEV"
# Allow netdevices to be re-created following the reload
sleep 20
log_test "bridge vlan flags"
}
vlan_1_test()
{
# Test that VLAN 1 cannot be configured, as it is used internally for
# untagged traffic. See commit 47bf9df2e820 ("mlxsw: spectrum: Forbid
# creation of VLAN 1 over port/LAG") for more details
RET=0
ip link add link $swp1 name $swp1.1 type vlan id 1 &> /dev/null
check_fail $? "managed to create vlan 1 when should not"
log_test "vlan 1"
}
lag_bridge_upper_test()
{
# Test that ports cannot be enslaved to LAG devices that have uppers
# and that failure is handled gracefully. See commit b3529af6bb0d
# ("spectrum: Reference count VLAN entries") for more details
RET=0
ip link add name bond1 type bond mode 802.3ad
ip link add name br0 type bridge vlan_filtering 1
ip link set dev bond1 master br0
ip link set dev $swp1 down
ip link set dev $swp1 master bond1 &> /dev/null
check_fail $? "managed to enslave port to lag when should not"
# This might generate a trace, if we did not handle the failure
# correctly
ip -6 address add 2001:db8:1::1/64 dev $swp1
ip -6 address del 2001:db8:1::1/64 dev $swp1
log_test "lag with bridge upper"
ip link del dev br0
ip link del dev bond1
}
duplicate_vlans_test()
{
# Test that on a given port a VLAN is only used once. Either as VLAN
# in a VLAN-aware bridge or as a VLAN device
RET=0
ip link add name br0 type bridge vlan_filtering 1
ip link set dev $swp1 master br0
bridge vlan add vid 10 dev $swp1
ip link add link $swp1 name $swp1.10 type vlan id 10 &> /dev/null
check_fail $? "managed to create vlan device when should not"
bridge vlan del vid 10 dev $swp1
ip link add link $swp1 name $swp1.10 type vlan id 10
check_err $? "did not manage to create vlan device when should"
bridge vlan add vid 10 dev $swp1 &> /dev/null
check_fail $? "managed to add bridge vlan when should not"
log_test "duplicate vlans"
ip link del dev $swp1.10
ip link del dev br0
}
vlan_rif_refcount_test()
{
# Test that RIFs representing VLAN interfaces are not affected from
# ports member in the VLAN. We use the offload indication on routes
# configured on the RIF to understand if it was created / destroyed
RET=0
ip link add name br0 type bridge vlan_filtering 1
ip link set dev $swp1 master br0
ip link set dev $swp1 up
ip link set dev br0 up
ip link add link br0 name br0.10 up type vlan id 10
ip -6 address add 2001:db8:1::1/64 dev br0.10
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10 | grep -q offload
check_err $? "vlan rif was not created before adding port to vlan"
bridge vlan add vid 10 dev $swp1
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10 | grep -q offload
check_err $? "vlan rif was destroyed after adding port to vlan"
bridge vlan del vid 10 dev $swp1
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10 | grep -q offload
check_err $? "vlan rif was destroyed after removing port from vlan"
ip link set dev $swp1 nomaster
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10 | grep -q offload
check_fail $? "vlan rif was not destroyed after unlinking port from bridge"
log_test "vlan rif refcount"
ip link del dev br0.10
ip link set dev $swp1 down
ip link del dev br0
}
subport_rif_refcount_test()
{
# Test that RIFs representing upper devices of physical ports are
# reference counted correctly and destroyed when should. We use the
# offload indication on routes configured on the RIF to understand if
# it was created / destroyed
RET=0
ip link add name bond1 type bond mode 802.3ad
ip link set dev $swp1 down
ip link set dev $swp2 down
ip link set dev $swp1 master bond1
ip link set dev $swp2 master bond1
ip link set dev bond1 up
ip link add link bond1 name bond1.10 up type vlan id 10
ip -6 address add 2001:db8:1::1/64 dev bond1
ip -6 address add 2001:db8:2::1/64 dev bond1.10
ip -6 route get fibmatch 2001:db8:1::2 dev bond1 | grep -q offload
check_err $? "subport rif was not created on lag device"
ip -6 route get fibmatch 2001:db8:2::2 dev bond1.10 | grep -q offload
check_err $? "subport rif was not created on vlan device"
ip link set dev $swp1 nomaster
ip -6 route get fibmatch 2001:db8:1::2 dev bond1 | grep -q offload
check_err $? "subport rif of lag device was destroyed when should not"
ip -6 route get fibmatch 2001:db8:2::2 dev bond1.10 | grep -q offload
check_err $? "subport rif of vlan device was destroyed when should not"
ip link set dev $swp2 nomaster
ip -6 route get fibmatch 2001:db8:1::2 dev bond1 | grep -q offload
check_fail $? "subport rif of lag device was not destroyed when should"
ip -6 route get fibmatch 2001:db8:2::2 dev bond1.10 | grep -q offload
check_fail $? "subport rif of vlan device was not destroyed when should"
log_test "subport rif refcount"
ip link del dev bond1.10
ip link del dev bond1
}
vlan_dev_deletion_test()
{
# Test that VLAN devices are correctly deleted / unlinked when enslaved
# to bridge
RET=0
ip link add name br10 type bridge
ip link add name br20 type bridge
ip link add name br30 type bridge
ip link add link $swp1 name $swp1.10 type vlan id 10
ip link add link $swp1 name $swp1.20 type vlan id 20
ip link add link $swp1 name $swp1.30 type vlan id 30
ip link set dev $swp1.10 master br10
ip link set dev $swp1.20 master br20
ip link set dev $swp1.30 master br30
# If we did not handle the situation correctly, then these operations
# might produce a trace
ip link set dev $swp1.30 nomaster
ip link del dev $swp1.20
# Deletion via ioctl uses different code paths from netlink
vconfig rem $swp1.10 &> /dev/null
log_test "vlan device deletion"
ip link del dev $swp1.30
ip link del dev br30
ip link del dev br20
ip link del dev br10
}
lag_create()
{
ip link add name bond1 type bond mode 802.3ad
ip link set dev $swp1 down
ip link set dev $swp2 down
ip link set dev $swp1 master bond1
ip link set dev $swp2 master bond1
ip link add link bond1 name bond1.10 type vlan id 10
ip link add link bond1 name bond1.20 type vlan id 20
ip link add name br0 type bridge vlan_filtering 1
ip link set dev bond1 master br0
ip link add name br10 type bridge
ip link set dev bond1.10 master br10
ip link add name br20 type bridge
ip link set dev bond1.20 master br20
}
lag_unlink_slaves_test()
{
# Test that ports are correctly unlinked from their LAG master, when
# the LAG and its VLAN uppers are enslaved to bridges
RET=0
lag_create
ip link set dev $swp1 nomaster
check_err $? "lag slave $swp1 was not unlinked from master"
ip link set dev $swp2 nomaster
check_err $? "lag slave $swp2 was not unlinked from master"
# Try to configure corresponding VLANs as router interfaces
ip -6 address add 2001:db8:1::1/64 dev $swp1
check_err $? "failed to configure ip address on $swp1"
ip link add link $swp1 name $swp1.10 type vlan id 10
ip -6 address add 2001:db8:10::1/64 dev $swp1.10
check_err $? "failed to configure ip address on $swp1.10"
ip link add link $swp1 name $swp1.20 type vlan id 20
ip -6 address add 2001:db8:20::1/64 dev $swp1.20
check_err $? "failed to configure ip address on $swp1.20"
log_test "lag slaves unlinking"
ip link del dev $swp1.20
ip link del dev $swp1.10
ip address flush dev $swp1
ip link del dev br20
ip link del dev br10
ip link del dev br0
ip link del dev bond1
}
lag_dev_deletion_test()
{
# Test that LAG device is correctly deleted, when the LAG and its VLAN
# uppers are enslaved to bridges
RET=0
lag_create
ip link del dev bond1
log_test "lag device deletion"
ip link del dev br20
ip link del dev br10
ip link del dev br0
}
vlan_interface_uppers_test()
{
# Test that uppers of a VLAN interface are correctly sanitized
RET=0
ip link add name br0 type bridge vlan_filtering 1
ip link set dev $swp1 master br0
ip link add link br0 name br0.10 type vlan id 10
ip link add link br0.10 name macvlan0 \
type macvlan mode private &> /dev/null
check_fail $? "managed to create a macvlan when should not"
ip -6 address add 2001:db8:1::1/64 dev br0.10
ip link add link br0.10 name macvlan0 type macvlan mode private
check_err $? "did not manage to create a macvlan when should"
ip link del dev macvlan0
ip link add name vrf-test type vrf table 10
ip link set dev br0.10 master vrf-test
check_err $? "did not manage to enslave vlan interface to vrf"
ip link del dev vrf-test
ip link add name br-test type bridge
ip link set dev br0.10 master br-test &> /dev/null
check_fail $? "managed to enslave vlan interface to bridge when should not"
ip link del dev br-test
log_test "vlan interface uppers"
ip link del dev br0
}
devlink_reload_test()
{
# Test that after executing all the above configuration tests, a
# devlink reload can be performed without errors
RET=0
devlink dev reload "$DEVLINK_DEV"
check_err $? "devlink reload failed"
log_test "devlink reload - last test"
sleep 20
}
trap cleanup EXIT
setup_prepare