From 9a0c48df0d77602da3958a4c8fc2abb9521b0ade Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 31 Aug 2017 16:16:12 -0700 Subject: [PATCH 1/2] netvsc: cleanup datapath switch Use one routine for datapath up/down. Don't need to reopen the rndis layer. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 38 ++++++--------------------------- 1 file changed, 7 insertions(+), 31 deletions(-) diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 05ee870c3636c..b54b5b99cd799 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1847,11 +1847,13 @@ static int netvsc_register_vf(struct net_device *vf_netdev) return NOTIFY_OK; } -static int netvsc_vf_up(struct net_device *vf_netdev) +/* VF up/down change detected, schedule to change data path */ +static int netvsc_vf_changed(struct net_device *vf_netdev) { struct net_device_context *net_device_ctx; struct netvsc_device *netvsc_dev; struct net_device *ndev; + bool vf_is_up = netif_running(vf_netdev); ndev = get_netvsc_byref(vf_netdev); if (!ndev) @@ -1862,34 +1864,9 @@ static int netvsc_vf_up(struct net_device *vf_netdev) if (!netvsc_dev) return NOTIFY_DONE; - /* Bump refcount when datapath is acvive - Why? */ - rndis_filter_open(netvsc_dev); - - /* notify the host to switch the data path. */ - netvsc_switch_datapath(ndev, true); - netdev_info(ndev, "Data path switched to VF: %s\n", vf_netdev->name); - - return NOTIFY_OK; -} - -static int netvsc_vf_down(struct net_device *vf_netdev) -{ - struct net_device_context *net_device_ctx; - struct netvsc_device *netvsc_dev; - struct net_device *ndev; - - ndev = get_netvsc_byref(vf_netdev); - if (!ndev) - return NOTIFY_DONE; - - net_device_ctx = netdev_priv(ndev); - netvsc_dev = rtnl_dereference(net_device_ctx->nvdev); - if (!netvsc_dev) - return NOTIFY_DONE; - - netvsc_switch_datapath(ndev, false); - netdev_info(ndev, "Data path switched from VF: %s\n", vf_netdev->name); - rndis_filter_close(netvsc_dev); + netvsc_switch_datapath(ndev, vf_is_up); + netdev_info(ndev, "Data path switched %s VF: %s\n", + vf_is_up ? "to" : "from", vf_netdev->name); return NOTIFY_OK; } @@ -2099,9 +2076,8 @@ static int netvsc_netdev_event(struct notifier_block *this, case NETDEV_UNREGISTER: return netvsc_unregister_vf(event_dev); case NETDEV_UP: - return netvsc_vf_up(event_dev); case NETDEV_DOWN: - return netvsc_vf_down(event_dev); + return netvsc_vf_changed(event_dev); default: return NOTIFY_DONE; } From ec158f77def2df084d9f62565357e3037b04bd3f Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 31 Aug 2017 16:16:13 -0700 Subject: [PATCH 2/2] netvsc: allow driver to be removed even if VF is present If VF is attached then can still allow netvsc driver module to be removed. Just have to make sure and do the cleanup. Also, avoid extra rtnl round trip when calling unregister. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index b54b5b99cd799..3aee4b68ff138 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1839,9 +1839,6 @@ static int netvsc_register_vf(struct net_device *vf_netdev) netdev_info(ndev, "VF registering: %s\n", vf_netdev->name); - /* Prevent this module from being unloaded while VF is registered */ - try_module_get(THIS_MODULE); - dev_hold(vf_netdev); rcu_assign_pointer(net_device_ctx->vf_netdev, vf_netdev); return NOTIFY_OK; @@ -1885,10 +1882,11 @@ static int netvsc_unregister_vf(struct net_device *vf_netdev) netdev_info(ndev, "VF unregistering: %s\n", vf_netdev->name); + netdev_rx_handler_unregister(vf_netdev); netdev_upper_dev_unlink(vf_netdev, ndev); RCU_INIT_POINTER(net_device_ctx->vf_netdev, NULL); dev_put(vf_netdev); - module_put(THIS_MODULE); + return NOTIFY_OK; } @@ -1992,11 +1990,11 @@ static int netvsc_probe(struct hv_device *dev, static int netvsc_remove(struct hv_device *dev) { - struct net_device *net; struct net_device_context *ndev_ctx; + struct net_device *vf_netdev; + struct net_device *net; net = hv_get_drvdata(dev); - if (net == NULL) { dev_err(&dev->device, "No net device to remove\n"); return 0; @@ -2013,12 +2011,15 @@ static int netvsc_remove(struct hv_device *dev) * removed. Also blocks mtu and channel changes. */ rtnl_lock(); + vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev); + if (vf_netdev) + netvsc_unregister_vf(vf_netdev); + rndis_filter_device_remove(dev, rtnl_dereference(ndev_ctx->nvdev)); + unregister_netdevice(net); rtnl_unlock(); - unregister_netdev(net); - hv_set_drvdata(dev, NULL); free_percpu(ndev_ctx->vf_stats);