mlxsw: spectrum: Forbid linking to devices that have uppers
authorIdo Schimmel <idosch@mellanox.com>
Fri, 1 Sep 2017 08:52:31 +0000 (10:52 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 20 Sep 2017 06:19:55 +0000 (08:19 +0200)
[ Upstream commit 25cc72a33835ed8a6f53180a822cadab855852ac ]

The mlxsw driver relies on NETDEV_CHANGEUPPER events to configure the
device in case a port is enslaved to a master netdev such as bridge or
bond.

Since the driver ignores events unrelated to its ports and their
uppers, it's possible to engineer situations in which the device's data
path differs from the kernel's.

One example to such a situation is when a port is enslaved to a bond
that is already enslaved to a bridge. When the bond was enslaved the
driver ignored the event - as the bond wasn't one of its uppers - and
therefore a bridge port instance isn't created in the device.

Until such configurations are supported forbid them by checking that the
upper device doesn't have uppers of its own.

Fixes: 0d65fc13042f ("mlxsw: spectrum: Implement LAG port join/leave")
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Reported-by: Nogah Frankel <nogahf@mellanox.com>
Tested-by: Nogah Frankel <nogahf@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/net/ethernet/mellanox/mlxsw/spectrum.c
include/linux/netdevice.h
net/core/dev.c

index f902c4d3de9981a0e783249fa30aefafb19c16fb..1806b1fc6e4c98f8f15e187ea46fe856bb31984f 100644 (file)
@@ -4172,6 +4172,8 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev,
                        return -EINVAL;
                if (!info->linking)
                        break;
+               if (netdev_has_any_upper_dev(upper_dev))
+                       return -EINVAL;
                /* HW limitation forbids to put ports to multiple bridges. */
                if (netif_is_bridge_master(upper_dev) &&
                    !mlxsw_sp_master_bridge_check(mlxsw_sp, upper_dev))
@@ -4185,6 +4187,10 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev,
                if (netif_is_lag_port(dev) && is_vlan_dev(upper_dev) &&
                    !netif_is_lag_master(vlan_dev_real_dev(upper_dev)))
                        return -EINVAL;
+               if (!info->linking)
+                       break;
+               if (netdev_has_any_upper_dev(upper_dev))
+                       return -EINVAL;
                break;
        case NETDEV_CHANGEUPPER:
                upper_dev = info->upper_dev;
index 780e7171f54817353434be44b2ad5f62991c4800..23db1ae374648b9f39b74e863ce1545d2cfe96de 100644 (file)
@@ -3901,6 +3901,8 @@ struct net_device *netdev_all_upper_get_next_dev_rcu(struct net_device *dev,
             updev; \
             updev = netdev_all_upper_get_next_dev_rcu(dev, &(iter)))
 
+bool netdev_has_any_upper_dev(struct net_device *dev);
+
 void *netdev_lower_get_next_private(struct net_device *dev,
                                    struct list_head **iter);
 void *netdev_lower_get_next_private_rcu(struct net_device *dev,
index 1d0a7369d5a2257622883a621996742a4f80e785..ba7b8121a41414766041282783c01e0365da68ef 100644 (file)
@@ -5337,12 +5337,13 @@ EXPORT_SYMBOL(netdev_has_upper_dev);
  * Find out if a device is linked to an upper device and return true in case
  * it is. The caller must hold the RTNL lock.
  */
-static bool netdev_has_any_upper_dev(struct net_device *dev)
+bool netdev_has_any_upper_dev(struct net_device *dev)
 {
        ASSERT_RTNL();
 
        return !list_empty(&dev->all_adj_list.upper);
 }
+EXPORT_SYMBOL(netdev_has_any_upper_dev);
 
 /**
  * netdev_master_upper_dev_get - Get master upper device