Jelajahi Sumber

hv_netvsc: fix network namespace issues with VF support

When finding the parent netvsc device, the search needs to be across
all netvsc device instances (independent of network namespace).

Find parent device of VF using upper_dev_get routine which
searches only adjacent list.

Fixes: e8ff40d4bff1 ("hv_netvsc: improve VF device matching")
Signed-off-by: Stephen Hemminger <sthemmin@microsoft.com>

netns aware byref
Signed-off-by: David S. Miller <davem@davemloft.net>
Stephen Hemminger 7 tahun lalu
induk
melakukan
7bf7bb37f1
2 mengubah file dengan 22 tambahan dan 23 penghapusan
  1. 2 0
      drivers/net/hyperv/hyperv_net.h
  2. 20 23
      drivers/net/hyperv/netvsc_drv.c

+ 2 - 0
drivers/net/hyperv/hyperv_net.h

@@ -901,6 +901,8 @@ struct net_device_context {
 	struct hv_device *device_ctx;
 	struct hv_device *device_ctx;
 	/* netvsc_device */
 	/* netvsc_device */
 	struct netvsc_device __rcu *nvdev;
 	struct netvsc_device __rcu *nvdev;
+	/* list of netvsc net_devices */
+	struct list_head list;
 	/* reconfigure work */
 	/* reconfigure work */
 	struct delayed_work dwork;
 	struct delayed_work dwork;
 	/* last reconfig time */
 	/* last reconfig time */

+ 20 - 23
drivers/net/hyperv/netvsc_drv.c

@@ -67,6 +67,8 @@ static int debug = -1;
 module_param(debug, int, 0444);
 module_param(debug, int, 0444);
 MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
 MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
 
 
+static LIST_HEAD(netvsc_dev_list);
+
 static void netvsc_change_rx_flags(struct net_device *net, int change)
 static void netvsc_change_rx_flags(struct net_device *net, int change)
 {
 {
 	struct net_device_context *ndev_ctx = netdev_priv(net);
 	struct net_device_context *ndev_ctx = netdev_priv(net);
@@ -1781,13 +1783,10 @@ out_unlock:
 
 
 static struct net_device *get_netvsc_bymac(const u8 *mac)
 static struct net_device *get_netvsc_bymac(const u8 *mac)
 {
 {
-	struct net_device *dev;
-
-	ASSERT_RTNL();
+	struct net_device_context *ndev_ctx;
 
 
-	for_each_netdev(&init_net, dev) {
-		if (dev->netdev_ops != &device_ops)
-			continue;	/* not a netvsc device */
+	list_for_each_entry(ndev_ctx, &netvsc_dev_list, list) {
+		struct net_device *dev = hv_get_drvdata(ndev_ctx->device_ctx);
 
 
 		if (ether_addr_equal(mac, dev->perm_addr))
 		if (ether_addr_equal(mac, dev->perm_addr))
 			return dev;
 			return dev;
@@ -1798,25 +1797,18 @@ static struct net_device *get_netvsc_bymac(const u8 *mac)
 
 
 static struct net_device *get_netvsc_byref(struct net_device *vf_netdev)
 static struct net_device *get_netvsc_byref(struct net_device *vf_netdev)
 {
 {
+	struct net_device_context *net_device_ctx;
 	struct net_device *dev;
 	struct net_device *dev;
 
 
-	ASSERT_RTNL();
-
-	for_each_netdev(&init_net, dev) {
-		struct net_device_context *net_device_ctx;
+	dev = netdev_master_upper_dev_get(vf_netdev);
+	if (!dev || dev->netdev_ops != &device_ops)
+		return NULL;	/* not a netvsc device */
 
 
-		if (dev->netdev_ops != &device_ops)
-			continue;	/* not a netvsc device */
+	net_device_ctx = netdev_priv(dev);
+	if (!rtnl_dereference(net_device_ctx->nvdev))
+		return NULL;	/* device is removed */
 
 
-		net_device_ctx = netdev_priv(dev);
-		if (!rtnl_dereference(net_device_ctx->nvdev))
-			continue;	/* device is removed */
-
-		if (rtnl_dereference(net_device_ctx->vf_netdev) == vf_netdev)
-			return dev;	/* a match */
-	}
-
-	return NULL;
+	return dev;
 }
 }
 
 
 /* Called when VF is injecting data into network stack.
 /* Called when VF is injecting data into network stack.
@@ -2093,15 +2085,19 @@ static int netvsc_probe(struct hv_device *dev,
 	else
 	else
 		net->max_mtu = ETH_DATA_LEN;
 		net->max_mtu = ETH_DATA_LEN;
 
 
-	ret = register_netdev(net);
+	rtnl_lock();
+	ret = register_netdevice(net);
 	if (ret != 0) {
 	if (ret != 0) {
 		pr_err("Unable to register netdev.\n");
 		pr_err("Unable to register netdev.\n");
 		goto register_failed;
 		goto register_failed;
 	}
 	}
 
 
-	return ret;
+	list_add(&net_device_ctx->list, &netvsc_dev_list);
+	rtnl_unlock();
+	return 0;
 
 
 register_failed:
 register_failed:
+	rtnl_unlock();
 	rndis_filter_device_remove(dev, nvdev);
 	rndis_filter_device_remove(dev, nvdev);
 rndis_failed:
 rndis_failed:
 	free_percpu(net_device_ctx->vf_stats);
 	free_percpu(net_device_ctx->vf_stats);
@@ -2147,6 +2143,7 @@ static int netvsc_remove(struct hv_device *dev)
 		rndis_filter_device_remove(dev, nvdev);
 		rndis_filter_device_remove(dev, nvdev);
 
 
 	unregister_netdevice(net);
 	unregister_netdevice(net);
+	list_del(&ndev_ctx->list);
 
 
 	rtnl_unlock();
 	rtnl_unlock();
 	rcu_read_unlock();
 	rcu_read_unlock();