Loading drivers/net/hyperv/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -2,5 +2,6 @@ config HYPERV_NET tristate "Microsoft Hyper-V virtual network driver" depends on HYPERV select UCS2_STRING select FAILOVER help Select this option to enable the Hyper-V virtual network driver. drivers/net/hyperv/hyperv_net.h +2 −0 Original line number Diff line number Diff line Loading @@ -932,6 +932,8 @@ struct net_device_context { u32 vf_alloc; /* Serial number of the VF to team with */ u32 vf_serial; struct failover *failover; }; /* Per channel data */ Loading drivers/net/hyperv/netvsc_drv.c +57 −165 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ #include <net/pkt_sched.h> #include <net/checksum.h> #include <net/ip6_checksum.h> #include <net/failover.h> #include "hyperv_net.h" Loading Loading @@ -1779,46 +1780,6 @@ static void netvsc_link_change(struct work_struct *w) rtnl_unlock(); } static struct net_device *get_netvsc_bymac(const u8 *mac) { struct net_device *dev; ASSERT_RTNL(); for_each_netdev(&init_net, dev) { if (dev->netdev_ops != &device_ops) continue; /* not a netvsc device */ if (ether_addr_equal(mac, dev->perm_addr)) return dev; } return NULL; } static struct net_device *get_netvsc_byref(struct net_device *vf_netdev) { struct net_device *dev; ASSERT_RTNL(); for_each_netdev(&init_net, dev) { struct net_device_context *net_device_ctx; if (dev->netdev_ops != &device_ops) continue; /* not a netvsc device */ net_device_ctx = netdev_priv(dev); if (!rtnl_dereference(net_device_ctx->nvdev)) continue; /* device is removed */ if (rtnl_dereference(net_device_ctx->vf_netdev) == vf_netdev) return dev; /* a match */ } return NULL; } /* Called when VF is injecting data into network stack. * Change the associated network device from VF to netvsc. * note: already called with rcu_read_lock Loading @@ -1841,46 +1802,6 @@ static rx_handler_result_t netvsc_vf_handle_frame(struct sk_buff **pskb) return RX_HANDLER_ANOTHER; } static int netvsc_vf_join(struct net_device *vf_netdev, struct net_device *ndev) { struct net_device_context *ndev_ctx = netdev_priv(ndev); int ret; ret = netdev_rx_handler_register(vf_netdev, netvsc_vf_handle_frame, ndev); if (ret != 0) { netdev_err(vf_netdev, "can not register netvsc VF receive handler (err = %d)\n", ret); goto rx_handler_failed; } ret = netdev_master_upper_dev_link(vf_netdev, ndev, NULL, NULL, NULL); if (ret != 0) { netdev_err(vf_netdev, "can not set master device %s (err = %d)\n", ndev->name, ret); goto upper_link_failed; } /* set slave flag before open to prevent IPv6 addrconf */ vf_netdev->flags |= IFF_SLAVE; schedule_delayed_work(&ndev_ctx->vf_takeover, VF_TAKEOVER_INT); call_netdevice_notifiers(NETDEV_JOIN, vf_netdev); netdev_info(vf_netdev, "joined to %s\n", ndev->name); return 0; upper_link_failed: netdev_rx_handler_unregister(vf_netdev); rx_handler_failed: return ret; } static void __netvsc_vf_setup(struct net_device *ndev, struct net_device *vf_netdev) { Loading Loading @@ -1931,85 +1852,95 @@ static void netvsc_vf_setup(struct work_struct *w) rtnl_unlock(); } static int netvsc_register_vf(struct net_device *vf_netdev) static int netvsc_pre_register_vf(struct net_device *vf_netdev, struct net_device *ndev) { struct net_device *ndev; struct net_device_context *net_device_ctx; struct netvsc_device *netvsc_dev; if (vf_netdev->addr_len != ETH_ALEN) return NOTIFY_DONE; /* * We will use the MAC address to locate the synthetic interface to * associate with the VF interface. If we don't find a matching * synthetic interface, move on. */ ndev = get_netvsc_bymac(vf_netdev->perm_addr); if (!ndev) return NOTIFY_DONE; net_device_ctx = netdev_priv(ndev); netvsc_dev = rtnl_dereference(net_device_ctx->nvdev); if (!netvsc_dev || rtnl_dereference(net_device_ctx->vf_netdev)) return NOTIFY_DONE; return -ENODEV; return 0; } static int netvsc_register_vf(struct net_device *vf_netdev, struct net_device *ndev) { struct net_device_context *ndev_ctx = netdev_priv(ndev); /* set slave flag before open to prevent IPv6 addrconf */ vf_netdev->flags |= IFF_SLAVE; schedule_delayed_work(&ndev_ctx->vf_takeover, VF_TAKEOVER_INT); if (netvsc_vf_join(vf_netdev, ndev) != 0) return NOTIFY_DONE; call_netdevice_notifiers(NETDEV_JOIN, vf_netdev); netdev_info(ndev, "VF registering: %s\n", vf_netdev->name); netdev_info(vf_netdev, "joined to %s\n", ndev->name); dev_hold(vf_netdev); rcu_assign_pointer(net_device_ctx->vf_netdev, vf_netdev); return NOTIFY_OK; rcu_assign_pointer(ndev_ctx->vf_netdev, vf_netdev); return 0; } /* VF up/down change detected, schedule to change data path */ static int netvsc_vf_changed(struct net_device *vf_netdev) static int netvsc_vf_changed(struct net_device *vf_netdev, struct net_device *ndev) { 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) return NOTIFY_DONE; net_device_ctx = netdev_priv(ndev); netvsc_dev = rtnl_dereference(net_device_ctx->nvdev); if (!netvsc_dev) return NOTIFY_DONE; return -ENODEV; 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; return 0; } static int netvsc_unregister_vf(struct net_device *vf_netdev) static int netvsc_pre_unregister_vf(struct net_device *vf_netdev, struct net_device *ndev) { struct net_device *ndev; struct net_device_context *net_device_ctx; ndev = get_netvsc_byref(vf_netdev); if (!ndev) return NOTIFY_DONE; net_device_ctx = netdev_priv(ndev); cancel_delayed_work_sync(&net_device_ctx->vf_takeover); return 0; } static int netvsc_unregister_vf(struct net_device *vf_netdev, struct net_device *ndev) { struct net_device_context *net_device_ctx; net_device_ctx = netdev_priv(ndev); 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); return NOTIFY_OK; return 0; } static struct failover_ops netvsc_failover_ops = { .slave_pre_register = netvsc_pre_register_vf, .slave_register = netvsc_register_vf, .slave_pre_unregister = netvsc_pre_unregister_vf, .slave_unregister = netvsc_unregister_vf, .slave_link_change = netvsc_vf_changed, .slave_handle_frame = netvsc_vf_handle_frame, }; static int netvsc_probe(struct hv_device *dev, const struct hv_vmbus_device_id *dev_id) { Loading Loading @@ -2099,8 +2030,14 @@ static int netvsc_probe(struct hv_device *dev, goto register_failed; } net_device_ctx->failover = failover_register(net, &netvsc_failover_ops); if (IS_ERR(net_device_ctx->failover)) goto err_failover; return ret; err_failover: unregister_netdev(net); register_failed: rndis_filter_device_remove(dev, nvdev); rndis_failed: Loading Loading @@ -2141,13 +2078,15 @@ static int netvsc_remove(struct hv_device *dev) rtnl_lock(); vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev); if (vf_netdev) netvsc_unregister_vf(vf_netdev); failover_slave_unregister(vf_netdev); if (nvdev) rndis_filter_device_remove(dev, nvdev); unregister_netdevice(net); failover_unregister(ndev_ctx->failover); rtnl_unlock(); rcu_read_unlock(); Loading @@ -2174,54 +2113,8 @@ static struct hv_driver netvsc_drv = { .remove = netvsc_remove, }; /* * On Hyper-V, every VF interface is matched with a corresponding * synthetic interface. The synthetic interface is presented first * to the guest. When the corresponding VF instance is registered, * we will take care of switching the data path. */ static int netvsc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { struct net_device *event_dev = netdev_notifier_info_to_dev(ptr); /* Skip our own events */ if (event_dev->netdev_ops == &device_ops) return NOTIFY_DONE; /* Avoid non-Ethernet type devices */ if (event_dev->type != ARPHRD_ETHER) return NOTIFY_DONE; /* Avoid Vlan dev with same MAC registering as VF */ if (is_vlan_dev(event_dev)) return NOTIFY_DONE; /* Avoid Bonding master dev with same MAC registering as VF */ if ((event_dev->priv_flags & IFF_BONDING) && (event_dev->flags & IFF_MASTER)) return NOTIFY_DONE; switch (event) { case NETDEV_REGISTER: return netvsc_register_vf(event_dev); case NETDEV_UNREGISTER: return netvsc_unregister_vf(event_dev); case NETDEV_UP: case NETDEV_DOWN: return netvsc_vf_changed(event_dev); default: return NOTIFY_DONE; } } static struct notifier_block netvsc_netdev_notifier = { .notifier_call = netvsc_netdev_event, }; static void __exit netvsc_drv_exit(void) { unregister_netdevice_notifier(&netvsc_netdev_notifier); vmbus_driver_unregister(&netvsc_drv); } Loading @@ -2241,7 +2134,6 @@ static int __init netvsc_drv_init(void) if (ret) return ret; register_netdevice_notifier(&netvsc_netdev_notifier); return 0; } Loading Loading
drivers/net/hyperv/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -2,5 +2,6 @@ config HYPERV_NET tristate "Microsoft Hyper-V virtual network driver" depends on HYPERV select UCS2_STRING select FAILOVER help Select this option to enable the Hyper-V virtual network driver.
drivers/net/hyperv/hyperv_net.h +2 −0 Original line number Diff line number Diff line Loading @@ -932,6 +932,8 @@ struct net_device_context { u32 vf_alloc; /* Serial number of the VF to team with */ u32 vf_serial; struct failover *failover; }; /* Per channel data */ Loading
drivers/net/hyperv/netvsc_drv.c +57 −165 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ #include <net/pkt_sched.h> #include <net/checksum.h> #include <net/ip6_checksum.h> #include <net/failover.h> #include "hyperv_net.h" Loading Loading @@ -1779,46 +1780,6 @@ static void netvsc_link_change(struct work_struct *w) rtnl_unlock(); } static struct net_device *get_netvsc_bymac(const u8 *mac) { struct net_device *dev; ASSERT_RTNL(); for_each_netdev(&init_net, dev) { if (dev->netdev_ops != &device_ops) continue; /* not a netvsc device */ if (ether_addr_equal(mac, dev->perm_addr)) return dev; } return NULL; } static struct net_device *get_netvsc_byref(struct net_device *vf_netdev) { struct net_device *dev; ASSERT_RTNL(); for_each_netdev(&init_net, dev) { struct net_device_context *net_device_ctx; if (dev->netdev_ops != &device_ops) continue; /* not a netvsc device */ net_device_ctx = netdev_priv(dev); if (!rtnl_dereference(net_device_ctx->nvdev)) continue; /* device is removed */ if (rtnl_dereference(net_device_ctx->vf_netdev) == vf_netdev) return dev; /* a match */ } return NULL; } /* Called when VF is injecting data into network stack. * Change the associated network device from VF to netvsc. * note: already called with rcu_read_lock Loading @@ -1841,46 +1802,6 @@ static rx_handler_result_t netvsc_vf_handle_frame(struct sk_buff **pskb) return RX_HANDLER_ANOTHER; } static int netvsc_vf_join(struct net_device *vf_netdev, struct net_device *ndev) { struct net_device_context *ndev_ctx = netdev_priv(ndev); int ret; ret = netdev_rx_handler_register(vf_netdev, netvsc_vf_handle_frame, ndev); if (ret != 0) { netdev_err(vf_netdev, "can not register netvsc VF receive handler (err = %d)\n", ret); goto rx_handler_failed; } ret = netdev_master_upper_dev_link(vf_netdev, ndev, NULL, NULL, NULL); if (ret != 0) { netdev_err(vf_netdev, "can not set master device %s (err = %d)\n", ndev->name, ret); goto upper_link_failed; } /* set slave flag before open to prevent IPv6 addrconf */ vf_netdev->flags |= IFF_SLAVE; schedule_delayed_work(&ndev_ctx->vf_takeover, VF_TAKEOVER_INT); call_netdevice_notifiers(NETDEV_JOIN, vf_netdev); netdev_info(vf_netdev, "joined to %s\n", ndev->name); return 0; upper_link_failed: netdev_rx_handler_unregister(vf_netdev); rx_handler_failed: return ret; } static void __netvsc_vf_setup(struct net_device *ndev, struct net_device *vf_netdev) { Loading Loading @@ -1931,85 +1852,95 @@ static void netvsc_vf_setup(struct work_struct *w) rtnl_unlock(); } static int netvsc_register_vf(struct net_device *vf_netdev) static int netvsc_pre_register_vf(struct net_device *vf_netdev, struct net_device *ndev) { struct net_device *ndev; struct net_device_context *net_device_ctx; struct netvsc_device *netvsc_dev; if (vf_netdev->addr_len != ETH_ALEN) return NOTIFY_DONE; /* * We will use the MAC address to locate the synthetic interface to * associate with the VF interface. If we don't find a matching * synthetic interface, move on. */ ndev = get_netvsc_bymac(vf_netdev->perm_addr); if (!ndev) return NOTIFY_DONE; net_device_ctx = netdev_priv(ndev); netvsc_dev = rtnl_dereference(net_device_ctx->nvdev); if (!netvsc_dev || rtnl_dereference(net_device_ctx->vf_netdev)) return NOTIFY_DONE; return -ENODEV; return 0; } static int netvsc_register_vf(struct net_device *vf_netdev, struct net_device *ndev) { struct net_device_context *ndev_ctx = netdev_priv(ndev); /* set slave flag before open to prevent IPv6 addrconf */ vf_netdev->flags |= IFF_SLAVE; schedule_delayed_work(&ndev_ctx->vf_takeover, VF_TAKEOVER_INT); if (netvsc_vf_join(vf_netdev, ndev) != 0) return NOTIFY_DONE; call_netdevice_notifiers(NETDEV_JOIN, vf_netdev); netdev_info(ndev, "VF registering: %s\n", vf_netdev->name); netdev_info(vf_netdev, "joined to %s\n", ndev->name); dev_hold(vf_netdev); rcu_assign_pointer(net_device_ctx->vf_netdev, vf_netdev); return NOTIFY_OK; rcu_assign_pointer(ndev_ctx->vf_netdev, vf_netdev); return 0; } /* VF up/down change detected, schedule to change data path */ static int netvsc_vf_changed(struct net_device *vf_netdev) static int netvsc_vf_changed(struct net_device *vf_netdev, struct net_device *ndev) { 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) return NOTIFY_DONE; net_device_ctx = netdev_priv(ndev); netvsc_dev = rtnl_dereference(net_device_ctx->nvdev); if (!netvsc_dev) return NOTIFY_DONE; return -ENODEV; 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; return 0; } static int netvsc_unregister_vf(struct net_device *vf_netdev) static int netvsc_pre_unregister_vf(struct net_device *vf_netdev, struct net_device *ndev) { struct net_device *ndev; struct net_device_context *net_device_ctx; ndev = get_netvsc_byref(vf_netdev); if (!ndev) return NOTIFY_DONE; net_device_ctx = netdev_priv(ndev); cancel_delayed_work_sync(&net_device_ctx->vf_takeover); return 0; } static int netvsc_unregister_vf(struct net_device *vf_netdev, struct net_device *ndev) { struct net_device_context *net_device_ctx; net_device_ctx = netdev_priv(ndev); 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); return NOTIFY_OK; return 0; } static struct failover_ops netvsc_failover_ops = { .slave_pre_register = netvsc_pre_register_vf, .slave_register = netvsc_register_vf, .slave_pre_unregister = netvsc_pre_unregister_vf, .slave_unregister = netvsc_unregister_vf, .slave_link_change = netvsc_vf_changed, .slave_handle_frame = netvsc_vf_handle_frame, }; static int netvsc_probe(struct hv_device *dev, const struct hv_vmbus_device_id *dev_id) { Loading Loading @@ -2099,8 +2030,14 @@ static int netvsc_probe(struct hv_device *dev, goto register_failed; } net_device_ctx->failover = failover_register(net, &netvsc_failover_ops); if (IS_ERR(net_device_ctx->failover)) goto err_failover; return ret; err_failover: unregister_netdev(net); register_failed: rndis_filter_device_remove(dev, nvdev); rndis_failed: Loading Loading @@ -2141,13 +2078,15 @@ static int netvsc_remove(struct hv_device *dev) rtnl_lock(); vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev); if (vf_netdev) netvsc_unregister_vf(vf_netdev); failover_slave_unregister(vf_netdev); if (nvdev) rndis_filter_device_remove(dev, nvdev); unregister_netdevice(net); failover_unregister(ndev_ctx->failover); rtnl_unlock(); rcu_read_unlock(); Loading @@ -2174,54 +2113,8 @@ static struct hv_driver netvsc_drv = { .remove = netvsc_remove, }; /* * On Hyper-V, every VF interface is matched with a corresponding * synthetic interface. The synthetic interface is presented first * to the guest. When the corresponding VF instance is registered, * we will take care of switching the data path. */ static int netvsc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { struct net_device *event_dev = netdev_notifier_info_to_dev(ptr); /* Skip our own events */ if (event_dev->netdev_ops == &device_ops) return NOTIFY_DONE; /* Avoid non-Ethernet type devices */ if (event_dev->type != ARPHRD_ETHER) return NOTIFY_DONE; /* Avoid Vlan dev with same MAC registering as VF */ if (is_vlan_dev(event_dev)) return NOTIFY_DONE; /* Avoid Bonding master dev with same MAC registering as VF */ if ((event_dev->priv_flags & IFF_BONDING) && (event_dev->flags & IFF_MASTER)) return NOTIFY_DONE; switch (event) { case NETDEV_REGISTER: return netvsc_register_vf(event_dev); case NETDEV_UNREGISTER: return netvsc_unregister_vf(event_dev); case NETDEV_UP: case NETDEV_DOWN: return netvsc_vf_changed(event_dev); default: return NOTIFY_DONE; } } static struct notifier_block netvsc_netdev_notifier = { .notifier_call = netvsc_netdev_event, }; static void __exit netvsc_drv_exit(void) { unregister_netdevice_notifier(&netvsc_netdev_notifier); vmbus_driver_unregister(&netvsc_drv); } Loading @@ -2241,7 +2134,6 @@ static int __init netvsc_drv_init(void) if (ret) return ret; register_netdevice_notifier(&netvsc_netdev_notifier); return 0; } Loading