These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / net / hyperv / rndis_filter.c
index 9118cea..5931a79 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/netdevice.h>
 #include <linux/if_vlan.h>
 #include <linux/nls.h>
+#include <linux/vmalloc.h>
 
 #include "hyperv_net.h"
 
@@ -983,9 +984,16 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc)
        struct netvsc_device *nvscdev;
        u16 chn_index = new_sc->offermsg.offer.sub_channel_index;
        int ret;
+       unsigned long flags;
 
        nvscdev = hv_get_drvdata(new_sc->primary_channel->device_obj);
 
+       spin_lock_irqsave(&nvscdev->sc_lock, flags);
+       nvscdev->num_sc_offered--;
+       spin_unlock_irqrestore(&nvscdev->sc_lock, flags);
+       if (nvscdev->num_sc_offered == 0)
+               complete(&nvscdev->channel_init_wait);
+
        if (chn_index >= nvscdev->num_chn)
                return;
 
@@ -1013,6 +1021,11 @@ int rndis_filter_device_add(struct hv_device *dev,
        struct ndis_recv_scale_cap rsscap;
        u32 rsscap_size = sizeof(struct ndis_recv_scale_cap);
        u32 mtu, size;
+       u32 num_rss_qs;
+       u32 sc_delta;
+       const struct cpumask *node_cpu_mask;
+       u32 num_possible_rss_qs;
+       unsigned long flags;
 
        rndis_device = get_rndis_device();
        if (!rndis_device)
@@ -1035,6 +1048,8 @@ int rndis_filter_device_add(struct hv_device *dev,
        net_device->max_chn = 1;
        net_device->num_chn = 1;
 
+       spin_lock_init(&net_device->sc_lock);
+
        net_device->extension = rndis_device;
        rndis_device->net_dev = net_device;
 
@@ -1050,7 +1065,7 @@ int rndis_filter_device_add(struct hv_device *dev,
        ret = rndis_filter_query_device(rndis_device,
                                        RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
                                        &mtu, &size);
-       if (ret == 0 && size == sizeof(u32))
+       if (ret == 0 && size == sizeof(u32) && mtu < net_device->ndev->mtu)
                net_device->ndev->mtu = mtu;
 
        /* Get the mac address */
@@ -1100,9 +1115,26 @@ int rndis_filter_device_add(struct hv_device *dev,
        if (ret || rsscap.num_recv_que < 2)
                goto out;
 
+       num_rss_qs = min(device_info->max_num_vrss_chns, rsscap.num_recv_que);
+
        net_device->max_chn = rsscap.num_recv_que;
-       net_device->num_chn = (num_online_cpus() < rsscap.num_recv_que) ?
-                              num_online_cpus() : rsscap.num_recv_que;
+
+       /*
+        * We will limit the VRSS channels to the number CPUs in the NUMA node
+        * the primary channel is currently bound to.
+        */
+       node_cpu_mask = cpumask_of_node(cpu_to_node(dev->channel->target_cpu));
+       num_possible_rss_qs = cpumask_weight(node_cpu_mask);
+
+       /* We will use the given number of channels if available. */
+       if (device_info->num_chn && device_info->num_chn < net_device->max_chn)
+               net_device->num_chn = device_info->num_chn;
+       else
+               net_device->num_chn = min(num_possible_rss_qs, num_rss_qs);
+
+       num_rss_qs = net_device->num_chn - 1;
+       net_device->num_sc_offered = num_rss_qs;
+
        if (net_device->num_chn == 1)
                goto out;
 
@@ -1144,11 +1176,25 @@ int rndis_filter_device_add(struct hv_device *dev,
 
        ret = rndis_filter_set_rss_param(rndis_device, net_device->num_chn);
 
+       /*
+        * Wait for the host to send us the sub-channel offers.
+        */
+       spin_lock_irqsave(&net_device->sc_lock, flags);
+       sc_delta = num_rss_qs - (net_device->num_chn - 1);
+       net_device->num_sc_offered -= sc_delta;
+       spin_unlock_irqrestore(&net_device->sc_lock, flags);
+
+       while (net_device->num_sc_offered != 0) {
+               t = wait_for_completion_timeout(&net_device->channel_init_wait, 10*HZ);
+               if (t == 0)
+                       WARN(1, "Netvsc: Waiting for sub-channel processing");
+       }
 out:
        if (ret) {
                net_device->max_chn = 1;
                net_device->num_chn = 1;
        }
+
        return 0; /* return 0 because primary channel can be used alone */
 
 err_dev_remv: