Commit 4ab0edec authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 05572271 489c25f9
...@@ -1141,16 +1141,20 @@ static void mlxsw_sp_port_vlan_flush(struct mlxsw_sp_port *mlxsw_sp_port) ...@@ -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, list_for_each_entry_safe(mlxsw_sp_port_vlan, tmp,
&mlxsw_sp_port->vlans_list, list) &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) mlxsw_sp_port_vlan_create(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
{ {
struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
bool untagged = vid == 1; bool untagged = vid == 1;
int err; 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); err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true, untagged);
if (err) if (err)
return ERR_PTR(err); return ERR_PTR(err);
...@@ -1162,7 +1166,6 @@ mlxsw_sp_port_vlan_create(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) ...@@ -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->mlxsw_sp_port = mlxsw_sp_port;
mlxsw_sp_port_vlan->ref_count = 1;
mlxsw_sp_port_vlan->vid = vid; mlxsw_sp_port_vlan->vid = vid;
list_add(&mlxsw_sp_port_vlan->list, &mlxsw_sp_port->vlans_list); list_add(&mlxsw_sp_port_vlan->list, &mlxsw_sp_port->vlans_list);
...@@ -1173,44 +1176,19 @@ mlxsw_sp_port_vlan_create(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) ...@@ -1173,44 +1176,19 @@ mlxsw_sp_port_vlan_create(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
return ERR_PTR(err); return ERR_PTR(err);
} }
static void void mlxsw_sp_port_vlan_destroy(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
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; struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
u16 vid = mlxsw_sp_port_vlan->vid; u16 vid = mlxsw_sp_port_vlan->vid;
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) if (mlxsw_sp_port_vlan->bridge_port)
mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan); mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan);
else if (fid) else if (mlxsw_sp_port_vlan->fid)
mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan); mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
mlxsw_sp_port_vlan_destroy(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);
} }
static int mlxsw_sp_port_add_vid(struct net_device *dev, static int mlxsw_sp_port_add_vid(struct net_device *dev,
...@@ -1224,7 +1202,7 @@ static int mlxsw_sp_port_add_vid(struct net_device *dev, ...@@ -1224,7 +1202,7 @@ static int mlxsw_sp_port_add_vid(struct net_device *dev,
if (!vid) if (!vid)
return 0; 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, 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, ...@@ -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); mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
if (!mlxsw_sp_port_vlan) if (!mlxsw_sp_port_vlan)
return 0; return 0;
mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan); mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
return 0; return 0;
} }
...@@ -3198,12 +3176,12 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, ...@@ -3198,12 +3176,12 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
goto err_port_nve_init; 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)) { if (IS_ERR(mlxsw_sp_port_vlan)) {
dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to create VID 1\n", dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to create VID 1\n",
mlxsw_sp_port->local_port); mlxsw_sp_port->local_port);
err = PTR_ERR(mlxsw_sp_port_vlan); 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); 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, ...@@ -3224,8 +3202,8 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
err_register_netdev: err_register_netdev:
mlxsw_sp->ports[local_port] = NULL; mlxsw_sp->ports[local_port] = NULL;
mlxsw_sp_port_switchdev_fini(mlxsw_sp_port); mlxsw_sp_port_switchdev_fini(mlxsw_sp_port);
mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan); mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
err_port_vlan_get: err_port_vlan_create:
mlxsw_sp_port_nve_fini(mlxsw_sp_port); mlxsw_sp_port_nve_fini(mlxsw_sp_port);
err_port_nve_init: err_port_nve_init:
mlxsw_sp_tc_qdisc_fini(mlxsw_sp_port); 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) ...@@ -4520,6 +4498,25 @@ void mlxsw_sp_port_dev_put(struct mlxsw_sp_port *mlxsw_sp_port)
dev_put(mlxsw_sp_port->dev); 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) static int mlxsw_sp_lag_create(struct mlxsw_sp *mlxsw_sp, u16 lag_id)
{ {
char sldr_pl[MLXSW_REG_SLDR_LEN]; 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, ...@@ -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 */ /* Any VLANs configured on the port are no longer valid */
mlxsw_sp_port_vlan_flush(mlxsw_sp_port); 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) if (lag->ref_count == 1)
mlxsw_sp_lag_destroy(mlxsw_sp, lag_id); 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, ...@@ -4721,7 +4722,7 @@ static void mlxsw_sp_port_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port,
mlxsw_sp_port->lagged = 0; mlxsw_sp_port->lagged = 0;
lag->ref_count--; 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 */ /* Make sure untagged frames are allowed to ingress */
mlxsw_sp_port_pvid_set(mlxsw_sp_port, 1); 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, ...@@ -5000,6 +5001,16 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
} else if (netif_is_macvlan(upper_dev)) { } else if (netif_is_macvlan(upper_dev)) {
if (!info->linking) if (!info->linking)
mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev); 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; break;
} }
...@@ -5156,6 +5167,48 @@ static int mlxsw_sp_netdevice_lag_port_vlan_event(struct net_device *vlan_dev, ...@@ -5156,6 +5167,48 @@ static int mlxsw_sp_netdevice_lag_port_vlan_event(struct net_device *vlan_dev,
return 0; 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, static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev,
unsigned long event, void *ptr) unsigned long event, void *ptr)
{ {
...@@ -5169,6 +5222,9 @@ static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev, ...@@ -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, return mlxsw_sp_netdevice_lag_port_vlan_event(vlan_dev,
real_dev, event, real_dev, event,
ptr, vid); 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; return 0;
} }
...@@ -5358,18 +5414,10 @@ static struct notifier_block mlxsw_sp_inetaddr_valid_nb __read_mostly = { ...@@ -5358,18 +5414,10 @@ static struct notifier_block mlxsw_sp_inetaddr_valid_nb __read_mostly = {
.notifier_call = mlxsw_sp_inetaddr_valid_event, .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 = { static struct notifier_block mlxsw_sp_inet6addr_valid_nb __read_mostly = {
.notifier_call = mlxsw_sp_inet6addr_valid_event, .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[] = { static const struct pci_device_id mlxsw_sp1_pci_id_table[] = {
{PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM), 0}, {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM), 0},
{0, }, {0, },
...@@ -5395,9 +5443,7 @@ static int __init mlxsw_sp_module_init(void) ...@@ -5395,9 +5443,7 @@ static int __init mlxsw_sp_module_init(void)
int err; int err;
register_inetaddr_validator_notifier(&mlxsw_sp_inetaddr_valid_nb); 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_validator_notifier(&mlxsw_sp_inet6addr_valid_nb);
register_inet6addr_notifier(&mlxsw_sp_inet6addr_nb);
err = mlxsw_core_driver_register(&mlxsw_sp1_driver); err = mlxsw_core_driver_register(&mlxsw_sp1_driver);
if (err) if (err)
...@@ -5424,9 +5470,7 @@ static int __init mlxsw_sp_module_init(void) ...@@ -5424,9 +5470,7 @@ static int __init mlxsw_sp_module_init(void)
err_sp2_core_driver_register: err_sp2_core_driver_register:
mlxsw_core_driver_unregister(&mlxsw_sp1_driver); mlxsw_core_driver_unregister(&mlxsw_sp1_driver);
err_sp1_core_driver_register: err_sp1_core_driver_register:
unregister_inet6addr_notifier(&mlxsw_sp_inet6addr_nb);
unregister_inet6addr_validator_notifier(&mlxsw_sp_inet6addr_valid_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); unregister_inetaddr_validator_notifier(&mlxsw_sp_inetaddr_valid_nb);
return err; return err;
} }
...@@ -5437,9 +5481,7 @@ static void __exit mlxsw_sp_module_exit(void) ...@@ -5437,9 +5481,7 @@ static void __exit mlxsw_sp_module_exit(void)
mlxsw_pci_driver_unregister(&mlxsw_sp1_pci_driver); mlxsw_pci_driver_unregister(&mlxsw_sp1_pci_driver);
mlxsw_core_driver_unregister(&mlxsw_sp2_driver); mlxsw_core_driver_unregister(&mlxsw_sp2_driver);
mlxsw_core_driver_unregister(&mlxsw_sp1_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_inet6addr_validator_notifier(&mlxsw_sp_inet6addr_valid_nb);
unregister_inetaddr_notifier(&mlxsw_sp_inetaddr_nb);
unregister_inetaddr_validator_notifier(&mlxsw_sp_inetaddr_valid_nb); unregister_inetaddr_validator_notifier(&mlxsw_sp_inetaddr_valid_nb);
} }
......
...@@ -190,7 +190,6 @@ struct mlxsw_sp_port_vlan { ...@@ -190,7 +190,6 @@ struct mlxsw_sp_port_vlan {
struct list_head list; struct list_head list;
struct mlxsw_sp_port *mlxsw_sp_port; struct mlxsw_sp_port *mlxsw_sp_port;
struct mlxsw_sp_fid *fid; struct mlxsw_sp_fid *fid;
unsigned int ref_count;
u16 vid; u16 vid;
struct mlxsw_sp_bridge_port *bridge_port; struct mlxsw_sp_bridge_port *bridge_port;
struct list_head bridge_vlan_node; 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, ...@@ -410,8 +409,8 @@ int mlxsw_sp_port_vid_learning_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid,
bool learn_enable); bool learn_enable);
int mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid); int mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid);
struct mlxsw_sp_port_vlan * struct mlxsw_sp_port_vlan *
mlxsw_sp_port_vlan_get(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid); mlxsw_sp_port_vlan_create(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid);
void mlxsw_sp_port_vlan_put(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan); 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, int mlxsw_sp_port_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin,
u16 vid_end, bool is_member, bool untagged); u16 vid_end, bool is_member, bool untagged);
int mlxsw_sp_flow_counter_get(struct mlxsw_sp *mlxsw_sp, 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, ...@@ -459,12 +458,8 @@ int mlxsw_sp_netdevice_router_port_event(struct net_device *dev,
unsigned long event, void *ptr); unsigned long event, void *ptr);
void mlxsw_sp_rif_macvlan_del(struct mlxsw_sp *mlxsw_sp, void mlxsw_sp_rif_macvlan_del(struct mlxsw_sp *mlxsw_sp,
const struct net_device *macvlan_dev); 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, int mlxsw_sp_inetaddr_valid_event(struct notifier_block *unused,
unsigned long event, void *ptr); 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, int mlxsw_sp_inet6addr_valid_event(struct notifier_block *unused,
unsigned long event, void *ptr); unsigned long event, void *ptr);
int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event, 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, ...@@ -484,7 +479,6 @@ mlxsw_sp_netdevice_ipip_ul_event(struct mlxsw_sp *mlxsw_sp,
struct netdev_notifier_info *info); struct netdev_notifier_info *info);
void void
mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan); 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, void mlxsw_sp_rif_destroy_by_dev(struct mlxsw_sp *mlxsw_sp,
struct net_device *dev); struct net_device *dev);
struct mlxsw_sp_rif *mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp, 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, ...@@ -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); struct mlxsw_sp_port *mlxsw_sp_port, u16 vid);
void mlxsw_sp_fid_port_vid_unmap(struct mlxsw_sp_fid *fid, void mlxsw_sp_fid_port_vid_unmap(struct mlxsw_sp_fid *fid,
struct mlxsw_sp_port *mlxsw_sp_port, u16 vid); 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); 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); 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); 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 enum mlxsw_sp_rif_type
mlxsw_sp_fid_type_rif_type(const struct mlxsw_sp *mlxsw_sp, mlxsw_sp_fid_type_rif_type(const struct mlxsw_sp *mlxsw_sp,
enum mlxsw_sp_fid_type type); enum mlxsw_sp_fid_type type);
......
...@@ -349,11 +349,6 @@ void mlxsw_sp_fid_port_vid_unmap(struct mlxsw_sp_fid *fid, ...@@ -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); 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) u16 mlxsw_sp_fid_index(const struct mlxsw_sp_fid *fid)
{ {
return fid->fid_index; return fid->fid_index;
...@@ -369,6 +364,11 @@ void mlxsw_sp_fid_rif_set(struct mlxsw_sp_fid *fid, struct mlxsw_sp_rif *rif) ...@@ -369,6 +364,11 @@ void mlxsw_sp_fid_rif_set(struct mlxsw_sp_fid *fid, struct mlxsw_sp_rif *rif)
fid->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 enum mlxsw_sp_rif_type
mlxsw_sp_fid_type_rif_type(const struct mlxsw_sp *mlxsw_sp, mlxsw_sp_fid_type_rif_type(const struct mlxsw_sp *mlxsw_sp,
enum mlxsw_sp_fid_type type) enum mlxsw_sp_fid_type type)
...@@ -1083,20 +1083,16 @@ void mlxsw_sp_fid_put(struct mlxsw_sp_fid *fid) ...@@ -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_fid_family *fid_family = fid->fid_family;
struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp; struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp;
if (--fid->ref_count == 1 && fid->rif) { if (--fid->ref_count != 0)
/* Destroy the associated RIF and let it drop the last return;
* reference on the FID.
*/ list_del(&fid->list);
return mlxsw_sp_rif_destroy(fid->rif); rhashtable_remove_fast(&mlxsw_sp->fid_core->fid_ht,
} else if (fid->ref_count == 0) { &fid->ht_node, mlxsw_sp_fid_ht_params);
list_del(&fid->list); fid->fid_family->ops->deconfigure(fid);
rhashtable_remove_fast(&mlxsw_sp->fid_core->fid_ht, __clear_bit(fid->fid_index - fid_family->start_index,
&fid->ht_node, mlxsw_sp_fid_ht_params); fid_family->fids_bitmap);
fid->fid_family->ops->deconfigure(fid); kfree(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) struct mlxsw_sp_fid *mlxsw_sp_fid_8021q_get(struct mlxsw_sp *mlxsw_sp, u16 vid)
......
...@@ -290,30 +290,6 @@ mlxsw_sp_bridge_port_destroy(struct mlxsw_sp_bridge_port *bridge_port) ...@@ -290,30 +290,6 @@ mlxsw_sp_bridge_port_destroy(struct mlxsw_sp_bridge_port *bridge_port)
kfree(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 * static struct mlxsw_sp_bridge_port *
mlxsw_sp_bridge_port_get(struct mlxsw_sp_bridge *bridge, mlxsw_sp_bridge_port_get(struct mlxsw_sp_bridge *bridge,
struct net_device *brport_dev) struct net_device *brport_dev)
...@@ -351,8 +327,7 @@ static void mlxsw_sp_bridge_port_put(struct mlxsw_sp_bridge *bridge, ...@@ -351,8 +327,7 @@ static void mlxsw_sp_bridge_port_put(struct mlxsw_sp_bridge *bridge,
{ {
struct mlxsw_sp_bridge_device *bridge_device; struct mlxsw_sp_bridge_device *bridge_device;
bridge_port->ref_count--; if (--bridge_port->ref_count != 0)
if (!mlxsw_sp_bridge_port_should_destroy(bridge_port))
return; return;
bridge_device = bridge_port->bridge_device; bridge_device = bridge_port->bridge_device;
mlxsw_sp_bridge_port_destroy(bridge_port); 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, ...@@ -1021,10 +996,8 @@ mlxsw_sp_port_vlan_bridge_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
int err; int err;
/* No need to continue if only VLAN flags were changed */ /* No need to continue if only VLAN flags were changed */
if (mlxsw_sp_port_vlan->bridge_port) { if (mlxsw_sp_port_vlan->bridge_port)
mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan);
return 0; return 0;
}
err = mlxsw_sp_port_vlan_fid_join(mlxsw_sp_port_vlan, bridge_port, err = mlxsw_sp_port_vlan_fid_join(mlxsw_sp_port_vlan, bridge_port,
extack); extack);
...@@ -1105,16 +1078,32 @@ static int ...@@ -1105,16 +1078,32 @@ static int
mlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_bridge_port *bridge_port, struct mlxsw_sp_bridge_port *bridge_port,
u16 vid, bool is_untagged, bool is_pvid, 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); u16 pvid = mlxsw_sp_port_pvid_determine(mlxsw_sp_port, vid, is_pvid);
struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
u16 old_pvid = mlxsw_sp_port->pvid; u16 old_pvid = mlxsw_sp_port->pvid;
int err; int err;
mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_get(mlxsw_sp_port, vid); /* The only valid scenario in which a port-vlan already exists, is if
if (IS_ERR(mlxsw_sp_port_vlan)) * the VLAN flags were changed and the port-vlan is associated with the
return PTR_ERR(mlxsw_sp_port_vlan); * 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, err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true,
is_untagged); is_untagged);
...@@ -1137,7 +1126,7 @@ mlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -1137,7 +1126,7 @@ mlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port,
err_port_pvid_set: err_port_pvid_set:
mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false); mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
err_port_vlan_set: err_port_vlan_set:
mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan); mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
return err; return err;
} }
...@@ -1199,9 +1188,6 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -1199,9 +1188,6 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
return err; return err;
} }
if (switchdev_trans_ph_prepare(trans))
return 0;
bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
if (WARN_ON(!bridge_port)) if (WARN_ON(!bridge_port))
return -EINVAL; return -EINVAL;
...@@ -1214,7 +1200,7 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -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, err = mlxsw_sp_bridge_port_vlan_add(mlxsw_sp_port, bridge_port,
vid, flag_untagged, vid, flag_untagged,
flag_pvid, extack); flag_pvid, extack, trans);
if (err) if (err)
return err; return err;
} }
...@@ -1832,7 +1818,7 @@ mlxsw_sp_bridge_port_vlan_del(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -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_vlan_bridge_leave(mlxsw_sp_port_vlan);
mlxsw_sp_port_pvid_set(mlxsw_sp_port, pvid); 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_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, 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, ...@@ -2000,7 +1986,7 @@ mlxsw_sp_bridge_8021q_port_join(struct mlxsw_sp_bridge_device *bridge_device,
return -EINVAL; return -EINVAL;
/* Let VLAN-aware bridge take care of its own VLANs */ /* 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; return 0;
} }
...@@ -2010,7 +1996,7 @@ mlxsw_sp_bridge_8021q_port_leave(struct mlxsw_sp_bridge_device *bridge_device, ...@@ -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_bridge_port *bridge_port,
struct mlxsw_sp_port *mlxsw_sp_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 */ /* Make sure untagged frames are allowed to ingress */
mlxsw_sp_port_pvid_set(mlxsw_sp_port, 1); mlxsw_sp_port_pvid_set(mlxsw_sp_port, 1);
} }
......
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