Commit 792df872 authored by Wenqi Ma's avatar Wenqi Ma Committed by David S. Miller

net/hyperv: Adding cancellation to ensure rndis filter is closed

Although the network interface is down, the RX packets number which
could be observed by ifconfig may keep on increasing.

This is because the WORK scheduled in netvsc_set_multicast_list()
may be executed after netvsc_close(). That means the rndis filter
may be re-enabled by do_set_multicast() even if it was closed by
netvsc_close().

By canceling possible WORK before close the rndis filter, the issue
could be never happened.
Signed-off-by: default avatarWenqi Ma <wenqi_ma@trendmicro.com.cn>
Reviewed-by: default avatarWei Yongjun <yongjun_wei@trendmicro.com.cn>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c5a99937
...@@ -44,6 +44,7 @@ struct net_device_context { ...@@ -44,6 +44,7 @@ struct net_device_context {
/* point back to our device context */ /* point back to our device context */
struct hv_device *device_ctx; struct hv_device *device_ctx;
struct delayed_work dwork; struct delayed_work dwork;
struct work_struct work;
}; };
...@@ -51,30 +52,22 @@ static int ring_size = 128; ...@@ -51,30 +52,22 @@ static int ring_size = 128;
module_param(ring_size, int, S_IRUGO); module_param(ring_size, int, S_IRUGO);
MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)"); MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)");
struct set_multicast_work {
struct work_struct work;
struct net_device *net;
};
static void do_set_multicast(struct work_struct *w) static void do_set_multicast(struct work_struct *w)
{ {
struct set_multicast_work *swk = struct net_device_context *ndevctx =
container_of(w, struct set_multicast_work, work); container_of(w, struct net_device_context, work);
struct net_device *net = swk->net;
struct net_device_context *ndevctx = netdev_priv(net);
struct netvsc_device *nvdev; struct netvsc_device *nvdev;
struct rndis_device *rdev; struct rndis_device *rdev;
nvdev = hv_get_drvdata(ndevctx->device_ctx); nvdev = hv_get_drvdata(ndevctx->device_ctx);
if (nvdev == NULL) if (nvdev == NULL || nvdev->ndev == NULL)
goto out; return;
rdev = nvdev->extension; rdev = nvdev->extension;
if (rdev == NULL) if (rdev == NULL)
goto out; return;
if (net->flags & IFF_PROMISC) if (nvdev->ndev->flags & IFF_PROMISC)
rndis_filter_set_packet_filter(rdev, rndis_filter_set_packet_filter(rdev,
NDIS_PACKET_TYPE_PROMISCUOUS); NDIS_PACKET_TYPE_PROMISCUOUS);
else else
...@@ -82,21 +75,13 @@ static void do_set_multicast(struct work_struct *w) ...@@ -82,21 +75,13 @@ static void do_set_multicast(struct work_struct *w)
NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_BROADCAST |
NDIS_PACKET_TYPE_ALL_MULTICAST | NDIS_PACKET_TYPE_ALL_MULTICAST |
NDIS_PACKET_TYPE_DIRECTED); NDIS_PACKET_TYPE_DIRECTED);
out:
kfree(w);
} }
static void netvsc_set_multicast_list(struct net_device *net) static void netvsc_set_multicast_list(struct net_device *net)
{ {
struct set_multicast_work *swk = struct net_device_context *net_device_ctx = netdev_priv(net);
kmalloc(sizeof(struct set_multicast_work), GFP_ATOMIC);
if (swk == NULL)
return;
swk->net = net; schedule_work(&net_device_ctx->work);
INIT_WORK(&swk->work, do_set_multicast);
schedule_work(&swk->work);
} }
static int netvsc_open(struct net_device *net) static int netvsc_open(struct net_device *net)
...@@ -125,6 +110,8 @@ static int netvsc_close(struct net_device *net) ...@@ -125,6 +110,8 @@ static int netvsc_close(struct net_device *net)
netif_tx_disable(net); netif_tx_disable(net);
/* Make sure netvsc_set_multicast_list doesn't re-enable filter! */
cancel_work_sync(&net_device_ctx->work);
ret = rndis_filter_close(device_obj); ret = rndis_filter_close(device_obj);
if (ret != 0) if (ret != 0)
netdev_err(net, "unable to close device (ret %d).\n", ret); netdev_err(net, "unable to close device (ret %d).\n", ret);
...@@ -335,6 +322,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) ...@@ -335,6 +322,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu)
nvdev->start_remove = true; nvdev->start_remove = true;
cancel_delayed_work_sync(&ndevctx->dwork); cancel_delayed_work_sync(&ndevctx->dwork);
cancel_work_sync(&ndevctx->work);
netif_tx_disable(ndev); netif_tx_disable(ndev);
rndis_filter_device_remove(hdev); rndis_filter_device_remove(hdev);
...@@ -403,6 +391,7 @@ static int netvsc_probe(struct hv_device *dev, ...@@ -403,6 +391,7 @@ static int netvsc_probe(struct hv_device *dev,
net_device_ctx->device_ctx = dev; net_device_ctx->device_ctx = dev;
hv_set_drvdata(dev, net); hv_set_drvdata(dev, net);
INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_send_garp); INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_send_garp);
INIT_WORK(&net_device_ctx->work, do_set_multicast);
net->netdev_ops = &device_ops; net->netdev_ops = &device_ops;
...@@ -456,6 +445,7 @@ static int netvsc_remove(struct hv_device *dev) ...@@ -456,6 +445,7 @@ static int netvsc_remove(struct hv_device *dev)
ndev_ctx = netdev_priv(net); ndev_ctx = netdev_priv(net);
cancel_delayed_work_sync(&ndev_ctx->dwork); cancel_delayed_work_sync(&ndev_ctx->dwork);
cancel_work_sync(&ndev_ctx->work);
/* Stop outbound asap */ /* Stop outbound asap */
netif_tx_disable(net); netif_tx_disable(net);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment