Merge remote-tracking branches 'asoc/topic/wm8753', 'asoc/topic/wm8770', 'asoc/topic...
[linux-block.git] / drivers / net / hyperv / netvsc_drv.c
index cdb78eefab671496d5b6c406c34b25e95278d14b..f28c85d212cee1ecfe2cf5f70717d4091acc5197 100644 (file)
 
 #include "hyperv_net.h"
 
-#define RING_SIZE_MIN          64
+#define RING_SIZE_MIN  64
+#define RETRY_US_LO    5000
+#define RETRY_US_HI    10000
+#define RETRY_MAX      2000    /* >10 sec */
 
 #define LINKCHANGE_INT (2 * HZ)
 #define VF_TAKEOVER_INT (HZ / 10)
@@ -89,15 +92,20 @@ static void netvsc_change_rx_flags(struct net_device *net, int change)
 static void netvsc_set_rx_mode(struct net_device *net)
 {
        struct net_device_context *ndev_ctx = netdev_priv(net);
-       struct net_device *vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev);
-       struct netvsc_device *nvdev = rtnl_dereference(ndev_ctx->nvdev);
+       struct net_device *vf_netdev;
+       struct netvsc_device *nvdev;
 
+       rcu_read_lock();
+       vf_netdev = rcu_dereference(ndev_ctx->vf_netdev);
        if (vf_netdev) {
                dev_uc_sync(vf_netdev, net);
                dev_mc_sync(vf_netdev, net);
        }
 
-       rndis_filter_update(nvdev);
+       nvdev = rcu_dereference(ndev_ctx->nvdev);
+       if (nvdev)
+               rndis_filter_update(nvdev);
+       rcu_read_unlock();
 }
 
 static int netvsc_open(struct net_device *net)
@@ -118,10 +126,8 @@ static int netvsc_open(struct net_device *net)
        }
 
        rdev = nvdev->extension;
-       if (!rdev->link_state) {
+       if (!rdev->link_state)
                netif_carrier_on(net);
-               netif_tx_wake_all_queues(net);
-       }
 
        if (vf_netdev) {
                /* Setting synthetic device up transparently sets
@@ -137,36 +143,25 @@ static int netvsc_open(struct net_device *net)
        return 0;
 }
 
-static int netvsc_close(struct net_device *net)
+static int netvsc_wait_until_empty(struct netvsc_device *nvdev)
 {
-       struct net_device_context *net_device_ctx = netdev_priv(net);
-       struct net_device *vf_netdev
-               = rtnl_dereference(net_device_ctx->vf_netdev);
-       struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev);
-       int ret = 0;
-       u32 aread, i, msec = 10, retry = 0, retry_max = 20;
-       struct vmbus_channel *chn;
-
-       netif_tx_disable(net);
-
-       /* No need to close rndis filter if it is removed already */
-       if (!nvdev)
-               goto out;
-
-       ret = rndis_filter_close(nvdev);
-       if (ret != 0) {
-               netdev_err(net, "unable to close device (ret %d).\n", ret);
-               return ret;
-       }
+       unsigned int retry = 0;
+       int i;
 
        /* Ensure pending bytes in ring are read */
-       while (true) {
-               aread = 0;
+       for (;;) {
+               u32 aread = 0;
+
                for (i = 0; i < nvdev->num_chn; i++) {
-                       chn = nvdev->chan_table[i].channel;
+                       struct vmbus_channel *chn
+                               = nvdev->chan_table[i].channel;
+
                        if (!chn)
                                continue;
 
+                       /* make sure receive not running now */
+                       napi_synchronize(&nvdev->chan_table[i].napi);
+
                        aread = hv_get_bytes_to_read(&chn->inbound);
                        if (aread)
                                break;
@@ -176,22 +171,40 @@ static int netvsc_close(struct net_device *net)
                                break;
                }
 
-               retry++;
-               if (retry > retry_max || aread == 0)
-                       break;
+               if (aread == 0)
+                       return 0;
 
-               msleep(msec);
+               if (++retry > RETRY_MAX)
+                       return -ETIMEDOUT;
 
-               if (msec < 1000)
-                       msec *= 2;
+               usleep_range(RETRY_US_LO, RETRY_US_HI);
        }
+}
 
-       if (aread) {
-               netdev_err(net, "Ring buffer not empty after closing rndis\n");
-               ret = -ETIMEDOUT;
+static int netvsc_close(struct net_device *net)
+{
+       struct net_device_context *net_device_ctx = netdev_priv(net);
+       struct net_device *vf_netdev
+               = rtnl_dereference(net_device_ctx->vf_netdev);
+       struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev);
+       int ret;
+
+       netif_tx_disable(net);
+
+       /* No need to close rndis filter if it is removed already */
+       if (!nvdev)
+               return 0;
+
+       ret = rndis_filter_close(nvdev);
+       if (ret != 0) {
+               netdev_err(net, "unable to close device (ret %d).\n", ret);
+               return ret;
        }
 
-out:
+       ret = netvsc_wait_until_empty(nvdev);
+       if (ret)
+               netdev_err(net, "Ring buffer not empty after closing rndis\n");
+
        if (vf_netdev)
                dev_close(vf_netdev);
 
@@ -840,16 +853,81 @@ static void netvsc_get_channels(struct net_device *net,
        }
 }
 
+static int netvsc_detach(struct net_device *ndev,
+                        struct netvsc_device *nvdev)
+{
+       struct net_device_context *ndev_ctx = netdev_priv(ndev);
+       struct hv_device *hdev = ndev_ctx->device_ctx;
+       int ret;
+
+       /* Don't try continuing to try and setup sub channels */
+       if (cancel_work_sync(&nvdev->subchan_work))
+               nvdev->num_chn = 1;
+
+       /* If device was up (receiving) then shutdown */
+       if (netif_running(ndev)) {
+               netif_tx_disable(ndev);
+
+               ret = rndis_filter_close(nvdev);
+               if (ret) {
+                       netdev_err(ndev,
+                                  "unable to close device (ret %d).\n", ret);
+                       return ret;
+               }
+
+               ret = netvsc_wait_until_empty(nvdev);
+               if (ret) {
+                       netdev_err(ndev,
+                                  "Ring buffer not empty after closing rndis\n");
+                       return ret;
+               }
+       }
+
+       netif_device_detach(ndev);
+
+       rndis_filter_device_remove(hdev, nvdev);
+
+       return 0;
+}
+
+static int netvsc_attach(struct net_device *ndev,
+                        struct netvsc_device_info *dev_info)
+{
+       struct net_device_context *ndev_ctx = netdev_priv(ndev);
+       struct hv_device *hdev = ndev_ctx->device_ctx;
+       struct netvsc_device *nvdev;
+       struct rndis_device *rdev;
+       int ret;
+
+       nvdev = rndis_filter_device_add(hdev, dev_info);
+       if (IS_ERR(nvdev))
+               return PTR_ERR(nvdev);
+
+       /* Note: enable and attach happen when sub-channels setup */
+
+       netif_carrier_off(ndev);
+
+       if (netif_running(ndev)) {
+               ret = rndis_filter_open(nvdev);
+               if (ret)
+                       return ret;
+
+               rdev = nvdev->extension;
+               if (!rdev->link_state)
+                       netif_carrier_on(ndev);
+       }
+
+       return 0;
+}
+
 static int netvsc_set_channels(struct net_device *net,
                               struct ethtool_channels *channels)
 {
        struct net_device_context *net_device_ctx = netdev_priv(net);
-       struct hv_device *dev = net_device_ctx->device_ctx;
        struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev);
        unsigned int orig, count = channels->combined_count;
        struct netvsc_device_info device_info;
-       bool was_opened;
-       int ret = 0;
+       int ret;
 
        /* We do not support separate count for rx, tx, or other */
        if (count == 0 ||
@@ -866,9 +944,6 @@ static int netvsc_set_channels(struct net_device *net,
                return -EINVAL;
 
        orig = nvdev->num_chn;
-       was_opened = rndis_filter_opened(nvdev);
-       if (was_opened)
-               rndis_filter_close(nvdev);
 
        memset(&device_info, 0, sizeof(device_info));
        device_info.num_chn = count;
@@ -877,28 +952,17 @@ static int netvsc_set_channels(struct net_device *net,
        device_info.recv_sections = nvdev->recv_section_cnt;
        device_info.recv_section_size = nvdev->recv_section_size;
 
-       rndis_filter_device_remove(dev, nvdev);
+       ret = netvsc_detach(net, nvdev);
+       if (ret)
+               return ret;
 
-       nvdev = rndis_filter_device_add(dev, &device_info);
-       if (IS_ERR(nvdev)) {
-               ret = PTR_ERR(nvdev);
+       ret = netvsc_attach(net, &device_info);
+       if (ret) {
                device_info.num_chn = orig;
-               nvdev = rndis_filter_device_add(dev, &device_info);
-
-               if (IS_ERR(nvdev)) {
-                       netdev_err(net, "restoring channel setting failed: %ld\n",
-                                  PTR_ERR(nvdev));
-                       return ret;
-               }
+               if (netvsc_attach(net, &device_info))
+                       netdev_err(net, "restoring channel setting failed\n");
        }
 
-       if (was_opened)
-               rndis_filter_open(nvdev);
-
-       /* We may have missed link change notifications */
-       net_device_ctx->last_reconfig = 0;
-       schedule_delayed_work(&net_device_ctx->dwork, 0);
-
        return ret;
 }
 
@@ -964,10 +1028,8 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu)
        struct net_device_context *ndevctx = netdev_priv(ndev);
        struct net_device *vf_netdev = rtnl_dereference(ndevctx->vf_netdev);
        struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev);
-       struct hv_device *hdev = ndevctx->device_ctx;
        int orig_mtu = ndev->mtu;
        struct netvsc_device_info device_info;
-       bool was_opened;
        int ret = 0;
 
        if (!nvdev || nvdev->destroy)
@@ -980,11 +1042,6 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu)
                        return ret;
        }
 
-       netif_device_detach(ndev);
-       was_opened = rndis_filter_opened(nvdev);
-       if (was_opened)
-               rndis_filter_close(nvdev);
-
        memset(&device_info, 0, sizeof(device_info));
        device_info.num_chn = nvdev->num_chn;
        device_info.send_sections = nvdev->send_section_cnt;
@@ -992,35 +1049,27 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu)
        device_info.recv_sections = nvdev->recv_section_cnt;
        device_info.recv_section_size = nvdev->recv_section_size;
 
-       rndis_filter_device_remove(hdev, nvdev);
+       ret = netvsc_detach(ndev, nvdev);
+       if (ret)
+               goto rollback_vf;
 
        ndev->mtu = mtu;
 
-       nvdev = rndis_filter_device_add(hdev, &device_info);
-       if (IS_ERR(nvdev)) {
-               ret = PTR_ERR(nvdev);
-
-               /* Attempt rollback to original MTU */
-               ndev->mtu = orig_mtu;
-               nvdev = rndis_filter_device_add(hdev, &device_info);
-
-               if (vf_netdev)
-                       dev_set_mtu(vf_netdev, orig_mtu);
-
-               if (IS_ERR(nvdev)) {
-                       netdev_err(ndev, "restoring mtu failed: %ld\n",
-                                  PTR_ERR(nvdev));
-                       return ret;
-               }
-       }
+       ret = netvsc_attach(ndev, &device_info);
+       if (ret)
+               goto rollback;
 
-       if (was_opened)
-               rndis_filter_open(nvdev);
+       return 0;
 
-       netif_device_attach(ndev);
+rollback:
+       /* Attempt rollback to original MTU */
+       ndev->mtu = orig_mtu;
 
-       /* We may have missed link change notifications */
-       schedule_delayed_work(&ndevctx->dwork, 0);
+       if (netvsc_attach(ndev, &device_info))
+               netdev_err(ndev, "restoring mtu failed\n");
+rollback_vf:
+       if (vf_netdev)
+               dev_set_mtu(vf_netdev, orig_mtu);
 
        return ret;
 }
@@ -1526,11 +1575,9 @@ static int netvsc_set_ringparam(struct net_device *ndev,
 {
        struct net_device_context *ndevctx = netdev_priv(ndev);
        struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev);
-       struct hv_device *hdev = ndevctx->device_ctx;
        struct netvsc_device_info device_info;
        struct ethtool_ringparam orig;
        u32 new_tx, new_rx;
-       bool was_opened;
        int ret = 0;
 
        if (!nvdev || nvdev->destroy)
@@ -1555,34 +1602,18 @@ static int netvsc_set_ringparam(struct net_device *ndev,
        device_info.recv_sections = new_rx;
        device_info.recv_section_size = nvdev->recv_section_size;
 
-       netif_device_detach(ndev);
-       was_opened = rndis_filter_opened(nvdev);
-       if (was_opened)
-               rndis_filter_close(nvdev);
-
-       rndis_filter_device_remove(hdev, nvdev);
-
-       nvdev = rndis_filter_device_add(hdev, &device_info);
-       if (IS_ERR(nvdev)) {
-               ret = PTR_ERR(nvdev);
+       ret = netvsc_detach(ndev, nvdev);
+       if (ret)
+               return ret;
 
+       ret = netvsc_attach(ndev, &device_info);
+       if (ret) {
                device_info.send_sections = orig.tx_pending;
                device_info.recv_sections = orig.rx_pending;
-               nvdev = rndis_filter_device_add(hdev, &device_info);
-               if (IS_ERR(nvdev)) {
-                       netdev_err(ndev, "restoring ringparam failed: %ld\n",
-                                  PTR_ERR(nvdev));
-                       return ret;
-               }
-       }
-
-       if (was_opened)
-               rndis_filter_open(nvdev);
-       netif_device_attach(ndev);
 
-       /* We may have missed link change notifications */
-       ndevctx->last_reconfig = 0;
-       schedule_delayed_work(&ndevctx->dwork, 0);
+               if (netvsc_attach(ndev, &device_info))
+                       netdev_err(ndev, "restoring ringparam failed");
+       }
 
        return ret;
 }
@@ -1846,8 +1877,12 @@ static void __netvsc_vf_setup(struct net_device *ndev,
 
        /* set multicast etc flags on VF */
        dev_change_flags(vf_netdev, ndev->flags | IFF_SLAVE);
+
+       /* sync address list from ndev to VF */
+       netif_addr_lock_bh(ndev);
        dev_uc_sync(vf_netdev, ndev);
        dev_mc_sync(vf_netdev, ndev);
+       netif_addr_unlock_bh(ndev);
 
        if (netif_running(ndev)) {
                ret = dev_open(vf_netdev);
@@ -2063,8 +2098,8 @@ no_net:
 static int netvsc_remove(struct hv_device *dev)
 {
        struct net_device_context *ndev_ctx;
-       struct net_device *vf_netdev;
-       struct net_device *net;
+       struct net_device *vf_netdev, *net;
+       struct netvsc_device *nvdev;
 
        net = hv_get_drvdata(dev);
        if (net == NULL) {
@@ -2074,10 +2109,14 @@ static int netvsc_remove(struct hv_device *dev)
 
        ndev_ctx = netdev_priv(net);
 
-       netif_device_detach(net);
-
        cancel_delayed_work_sync(&ndev_ctx->dwork);
 
+       rcu_read_lock();
+       nvdev = rcu_dereference(ndev_ctx->nvdev);
+
+       if  (nvdev)
+               cancel_work_sync(&nvdev->subchan_work);
+
        /*
         * Call to the vsc driver to let it know that the device is being
         * removed. Also blocks mtu and channel changes.
@@ -2087,11 +2126,13 @@ static int netvsc_remove(struct hv_device *dev)
        if (vf_netdev)
                netvsc_unregister_vf(vf_netdev);
 
+       if (nvdev)
+               rndis_filter_device_remove(dev, nvdev);
+
        unregister_netdevice(net);
 
-       rndis_filter_device_remove(dev,
-                                  rtnl_dereference(ndev_ctx->nvdev));
        rtnl_unlock();
+       rcu_read_unlock();
 
        hv_set_drvdata(dev, NULL);