Make sure that device is in detached state before doing ring
and mtu changes. When doing these changes, wait for all outstanding
send completions and ring buffer events.

Signed-off-by: Stephen Hemminger <step...@networkplumber.org>
---
 drivers/net/hyperv/hyperv_net.h   |  1 -
 drivers/net/hyperv/netvsc.c       |  6 +----
 drivers/net/hyperv/netvsc_drv.c   | 29 +++++++++++++----------
 drivers/net/hyperv/rndis_filter.c | 48 ++++++++++++++++++---------------------
 4 files changed, 40 insertions(+), 44 deletions(-)

diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
index 0db3bd1ea06f..a846a9c50ddb 100644
--- a/drivers/net/hyperv/hyperv_net.h
+++ b/drivers/net/hyperv/hyperv_net.h
@@ -211,7 +211,6 @@ void netvsc_channel_cb(void *context);
 int netvsc_poll(struct napi_struct *napi, int budget);
 
 void rndis_set_subchannel(struct work_struct *w);
-bool rndis_filter_opened(const struct netvsc_device *nvdev);
 int rndis_filter_open(struct netvsc_device *nvdev);
 int rndis_filter_close(struct netvsc_device *nvdev);
 struct netvsc_device *rndis_filter_device_add(struct hv_device *dev,
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 17e529af79dc..619a04f98321 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -849,7 +849,7 @@ int netvsc_send(struct net_device *ndev,
        bool try_batch, xmit_more;
 
        /* If device is rescinded, return error and packet will get dropped. */
-       if (unlikely(!net_device || net_device->destroy))
+       if (unlikely(!net_device))
                return -ENODEV;
 
        /* We may race with netvsc_connect_vsp()/netvsc_init_buf() and get
@@ -996,10 +996,6 @@ static int send_recv_completions(struct net_device *ndev,
                        mrc->first = 0;
        }
 
-       /* receive completion ring has been emptied */
-       if (unlikely(nvdev->destroy))
-               wake_up(&nvdev->wait_drain);
-
        return 0;
 }
 
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index c5584c2d440e..ef395e379a83 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -820,7 +820,7 @@ static int netvsc_set_channels(struct net_device *net,
            channels->rx_count || channels->tx_count || channels->other_count)
                return -EINVAL;
 
-       if (!nvdev || nvdev->destroy)
+       if (!nvdev)
                return -ENODEV;
 
        if (nvdev->nvsp_version < NVSP_PROTOCOL_VERSION_5)
@@ -830,9 +830,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;
@@ -841,6 +838,11 @@ 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;
 
+       was_opened = netif_running(net);
+       netif_device_detach(net);
+       if (was_opened)
+               rndis_filter_close(nvdev);
+
        rndis_filter_device_remove(dev, nvdev);
 
        nvdev = rndis_filter_device_add(dev, &device_info);
@@ -859,6 +861,8 @@ static int netvsc_set_channels(struct net_device *net,
        if (was_opened)
                rndis_filter_open(nvdev);
 
+       netif_device_attach(net);
+
        /* We may have missed link change notifications */
        net_device_ctx->last_reconfig = 0;
        schedule_delayed_work(&net_device_ctx->dwork, 0);
@@ -934,7 +938,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int 
mtu)
        bool was_opened;
        int ret = 0;
 
-       if (!nvdev || nvdev->destroy)
+       if (!nvdev)
                return -ENODEV;
 
        /* Change MTU of underlying VF netdev first. */
@@ -944,11 +948,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;
@@ -956,6 +955,11 @@ 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;
 
+       was_opened = netif_running(ndev);
+       netif_device_detach(ndev);
+       if (was_opened)
+               rndis_filter_close(nvdev);
+
        rndis_filter_device_remove(hdev, nvdev);
 
        ndev->mtu = mtu;
@@ -1497,7 +1501,7 @@ static int netvsc_set_ringparam(struct net_device *ndev,
        bool was_opened;
        int ret = 0;
 
-       if (!nvdev || nvdev->destroy)
+       if (!nvdev)
                return -ENODEV;
 
        memset(&orig, 0, sizeof(orig));
@@ -1519,8 +1523,8 @@ static int netvsc_set_ringparam(struct net_device *ndev,
        device_info.recv_sections = new_rx;
        device_info.recv_section_size = nvdev->recv_section_size;
 
+       was_opened = netif_running(ndev);
        netif_device_detach(ndev);
-       was_opened = rndis_filter_opened(nvdev);
        if (was_opened)
                rndis_filter_close(nvdev);
 
@@ -1542,6 +1546,7 @@ static int netvsc_set_ringparam(struct net_device *ndev,
 
        if (was_opened)
                rndis_filter_open(nvdev);
+
        netif_device_attach(ndev);
 
        /* We may have missed link change notifications */
diff --git a/drivers/net/hyperv/rndis_filter.c 
b/drivers/net/hyperv/rndis_filter.c
index c3ca191fea7f..a6c9c9a2973d 100644
--- a/drivers/net/hyperv/rndis_filter.c
+++ b/drivers/net/hyperv/rndis_filter.c
@@ -923,6 +923,7 @@ static int rndis_filter_init_device(struct rndis_device 
*dev,
        return ret;
 }
 
+/* Are there any outstanding sends */
 static bool netvsc_device_idle(const struct netvsc_device *nvdev)
 {
        int i;
@@ -930,8 +931,7 @@ static bool netvsc_device_idle(const struct netvsc_device 
*nvdev)
        for (i = 0; i < nvdev->num_chn; i++) {
                const struct netvsc_channel *nvchan = &nvdev->chan_table[i];
 
-               if (nvchan->mrc.first != nvchan->mrc.next)
-                       return false;
+               napi_synchronize(&nvchan->napi);
 
                if (atomic_read(&nvchan->queue_sends) > 0)
                        return false;
@@ -944,14 +944,12 @@ static void rndis_filter_halt_device(struct rndis_device 
*dev)
 {
        struct rndis_request *request;
        struct rndis_halt_request *halt;
-       struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
-       struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev);
 
        /* Attempt to do a rndis device halt */
        request = get_rndis_request(dev, RNDIS_MSG_HALT,
                                RNDIS_MESSAGE_SIZE(struct rndis_halt_request));
        if (!request)
-               goto cleanup;
+               return;
 
        /* Setup the rndis set */
        halt = &request->request_msg.msg.halt_req;
@@ -962,17 +960,7 @@ static void rndis_filter_halt_device(struct rndis_device 
*dev)
 
        dev->state = RNDIS_DEV_UNINITIALIZED;
 
-cleanup:
-       nvdev->destroy = true;
-
-       /* Force flag to be ordered before waiting */
-       wmb();
-
-       /* Wait for all send completions */
-       wait_event(nvdev->wait_drain, netvsc_device_idle(nvdev));
-
-       if (request)
-               put_rndis_request(dev, request);
+       put_rndis_request(dev, request);
 }
 
 static int rndis_filter_open_device(struct rndis_device *dev)
@@ -994,9 +982,11 @@ static int rndis_filter_open_device(struct rndis_device 
*dev)
 
 static int rndis_filter_close_device(struct rndis_device *dev)
 {
+       struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
+       struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev);
        int ret;
 
-       if (dev->state != RNDIS_DEV_DATAINITIALIZED)
+       if (!nvdev || dev->state != RNDIS_DEV_DATAINITIALIZED)
                return 0;
 
        /* Make sure rndis_set_multicast doesn't re-enable filter! */
@@ -1004,10 +994,23 @@ static int rndis_filter_close_device(struct rndis_device 
*dev)
 
        ret = rndis_filter_set_packet_filter(dev, 0);
        if (ret == -ENODEV)
-               ret = 0;
+               ret = 0;        /* rescinded is ok */
+
+       if (ret == 0) {
+               /* Indicate that wakeup on send done is desired */
+               nvdev->destroy = true;
+
+               /* Force flag to be ordered before waiting */
+               wmb();
+
+               /* Wait for all completions */
+               wait_event(nvdev->wait_drain, netvsc_device_idle(nvdev));
+
+               /* No more wakeups please */
+               nvdev->destroy = false;
 
-       if (ret == 0)
                dev->state = RNDIS_DEV_INITIALIZED;
+       }
 
        return ret;
 }
@@ -1364,10 +1367,3 @@ int rndis_filter_close(struct netvsc_device *nvdev)
 
        return rndis_filter_close_device(nvdev->extension);
 }
-
-bool rndis_filter_opened(const struct netvsc_device *nvdev)
-{
-       const struct rndis_device *dev = nvdev->extension;
-
-       return dev->state == RNDIS_DEV_DATAINITIALIZED;
-}
-- 
2.15.1

Reply via email to