diff options
Diffstat (limited to 'drivers/net/hyperv')
| -rw-r--r-- | drivers/net/hyperv/netvsc.c | 3 | ||||
| -rw-r--r-- | drivers/net/hyperv/netvsc_drv.c | 78 |
2 files changed, 49 insertions, 32 deletions
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 31c3d77b4733..fe01e141c8f8 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c | |||
| @@ -1203,6 +1203,9 @@ static void netvsc_send_vf(struct net_device *ndev, | |||
| 1203 | 1203 | ||
| 1204 | net_device_ctx->vf_alloc = nvmsg->msg.v4_msg.vf_assoc.allocated; | 1204 | net_device_ctx->vf_alloc = nvmsg->msg.v4_msg.vf_assoc.allocated; |
| 1205 | net_device_ctx->vf_serial = nvmsg->msg.v4_msg.vf_assoc.serial; | 1205 | net_device_ctx->vf_serial = nvmsg->msg.v4_msg.vf_assoc.serial; |
| 1206 | netdev_info(ndev, "VF slot %u %s\n", | ||
| 1207 | net_device_ctx->vf_serial, | ||
| 1208 | net_device_ctx->vf_alloc ? "added" : "removed"); | ||
| 1206 | } | 1209 | } |
| 1207 | 1210 | ||
| 1208 | static void netvsc_receive_inband(struct net_device *ndev, | 1211 | static void netvsc_receive_inband(struct net_device *ndev, |
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 1121a1ec407c..3af6d8d15233 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c | |||
| @@ -1894,20 +1894,6 @@ out_unlock: | |||
| 1894 | rtnl_unlock(); | 1894 | rtnl_unlock(); |
| 1895 | } | 1895 | } |
| 1896 | 1896 | ||
| 1897 | static struct net_device *get_netvsc_bymac(const u8 *mac) | ||
| 1898 | { | ||
| 1899 | struct net_device_context *ndev_ctx; | ||
| 1900 | |||
| 1901 | list_for_each_entry(ndev_ctx, &netvsc_dev_list, list) { | ||
| 1902 | struct net_device *dev = hv_get_drvdata(ndev_ctx->device_ctx); | ||
| 1903 | |||
| 1904 | if (ether_addr_equal(mac, dev->perm_addr)) | ||
| 1905 | return dev; | ||
| 1906 | } | ||
| 1907 | |||
| 1908 | return NULL; | ||
| 1909 | } | ||
| 1910 | |||
| 1911 | static struct net_device *get_netvsc_byref(struct net_device *vf_netdev) | 1897 | static struct net_device *get_netvsc_byref(struct net_device *vf_netdev) |
| 1912 | { | 1898 | { |
| 1913 | struct net_device_context *net_device_ctx; | 1899 | struct net_device_context *net_device_ctx; |
| @@ -2036,26 +2022,48 @@ static void netvsc_vf_setup(struct work_struct *w) | |||
| 2036 | rtnl_unlock(); | 2022 | rtnl_unlock(); |
| 2037 | } | 2023 | } |
| 2038 | 2024 | ||
| 2025 | /* Find netvsc by VMBus serial number. | ||
| 2026 | * The PCI hyperv controller records the serial number as the slot. | ||
| 2027 | */ | ||
| 2028 | static struct net_device *get_netvsc_byslot(const struct net_device *vf_netdev) | ||
| 2029 | { | ||
| 2030 | struct device *parent = vf_netdev->dev.parent; | ||
| 2031 | struct net_device_context *ndev_ctx; | ||
| 2032 | struct pci_dev *pdev; | ||
| 2033 | |||
| 2034 | if (!parent || !dev_is_pci(parent)) | ||
| 2035 | return NULL; /* not a PCI device */ | ||
| 2036 | |||
| 2037 | pdev = to_pci_dev(parent); | ||
| 2038 | if (!pdev->slot) { | ||
| 2039 | netdev_notice(vf_netdev, "no PCI slot information\n"); | ||
| 2040 | return NULL; | ||
| 2041 | } | ||
| 2042 | |||
| 2043 | list_for_each_entry(ndev_ctx, &netvsc_dev_list, list) { | ||
| 2044 | if (!ndev_ctx->vf_alloc) | ||
| 2045 | continue; | ||
| 2046 | |||
| 2047 | if (ndev_ctx->vf_serial == pdev->slot->number) | ||
| 2048 | return hv_get_drvdata(ndev_ctx->device_ctx); | ||
| 2049 | } | ||
| 2050 | |||
| 2051 | netdev_notice(vf_netdev, | ||
| 2052 | "no netdev found for slot %u\n", pdev->slot->number); | ||
| 2053 | return NULL; | ||
| 2054 | } | ||
| 2055 | |||
| 2039 | static int netvsc_register_vf(struct net_device *vf_netdev) | 2056 | static int netvsc_register_vf(struct net_device *vf_netdev) |
| 2040 | { | 2057 | { |
| 2041 | struct net_device *ndev; | ||
| 2042 | struct net_device_context *net_device_ctx; | 2058 | struct net_device_context *net_device_ctx; |
| 2043 | struct device *pdev = vf_netdev->dev.parent; | ||
| 2044 | struct netvsc_device *netvsc_dev; | 2059 | struct netvsc_device *netvsc_dev; |
| 2060 | struct net_device *ndev; | ||
| 2045 | int ret; | 2061 | int ret; |
| 2046 | 2062 | ||
| 2047 | if (vf_netdev->addr_len != ETH_ALEN) | 2063 | if (vf_netdev->addr_len != ETH_ALEN) |
| 2048 | return NOTIFY_DONE; | 2064 | return NOTIFY_DONE; |
| 2049 | 2065 | ||
| 2050 | if (!pdev || !dev_is_pci(pdev) || dev_is_pf(pdev)) | 2066 | ndev = get_netvsc_byslot(vf_netdev); |
| 2051 | return NOTIFY_DONE; | ||
| 2052 | |||
| 2053 | /* | ||
| 2054 | * We will use the MAC address to locate the synthetic interface to | ||
| 2055 | * associate with the VF interface. If we don't find a matching | ||
| 2056 | * synthetic interface, move on. | ||
| 2057 | */ | ||
| 2058 | ndev = get_netvsc_bymac(vf_netdev->perm_addr); | ||
| 2059 | if (!ndev) | 2067 | if (!ndev) |
| 2060 | return NOTIFY_DONE; | 2068 | return NOTIFY_DONE; |
| 2061 | 2069 | ||
| @@ -2206,6 +2214,16 @@ static int netvsc_probe(struct hv_device *dev, | |||
| 2206 | 2214 | ||
| 2207 | memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN); | 2215 | memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN); |
| 2208 | 2216 | ||
| 2217 | /* We must get rtnl lock before scheduling nvdev->subchan_work, | ||
| 2218 | * otherwise netvsc_subchan_work() can get rtnl lock first and wait | ||
| 2219 | * all subchannels to show up, but that may not happen because | ||
| 2220 | * netvsc_probe() can't get rtnl lock and as a result vmbus_onoffer() | ||
| 2221 | * -> ... -> device_add() -> ... -> __device_attach() can't get | ||
| 2222 | * the device lock, so all the subchannels can't be processed -- | ||
| 2223 | * finally netvsc_subchan_work() hangs for ever. | ||
| 2224 | */ | ||
| 2225 | rtnl_lock(); | ||
| 2226 | |||
| 2209 | if (nvdev->num_chn > 1) | 2227 | if (nvdev->num_chn > 1) |
| 2210 | schedule_work(&nvdev->subchan_work); | 2228 | schedule_work(&nvdev->subchan_work); |
| 2211 | 2229 | ||
| @@ -2224,7 +2242,6 @@ static int netvsc_probe(struct hv_device *dev, | |||
| 2224 | else | 2242 | else |
| 2225 | net->max_mtu = ETH_DATA_LEN; | 2243 | net->max_mtu = ETH_DATA_LEN; |
| 2226 | 2244 | ||
| 2227 | rtnl_lock(); | ||
| 2228 | ret = register_netdevice(net); | 2245 | ret = register_netdevice(net); |
| 2229 | if (ret != 0) { | 2246 | if (ret != 0) { |
| 2230 | pr_err("Unable to register netdev.\n"); | 2247 | pr_err("Unable to register netdev.\n"); |
| @@ -2263,17 +2280,15 @@ static int netvsc_remove(struct hv_device *dev) | |||
| 2263 | 2280 | ||
| 2264 | cancel_delayed_work_sync(&ndev_ctx->dwork); | 2281 | cancel_delayed_work_sync(&ndev_ctx->dwork); |
| 2265 | 2282 | ||
| 2266 | rcu_read_lock(); | 2283 | rtnl_lock(); |
| 2267 | nvdev = rcu_dereference(ndev_ctx->nvdev); | 2284 | nvdev = rtnl_dereference(ndev_ctx->nvdev); |
| 2268 | 2285 | if (nvdev) | |
| 2269 | if (nvdev) | ||
| 2270 | cancel_work_sync(&nvdev->subchan_work); | 2286 | cancel_work_sync(&nvdev->subchan_work); |
| 2271 | 2287 | ||
| 2272 | /* | 2288 | /* |
| 2273 | * Call to the vsc driver to let it know that the device is being | 2289 | * Call to the vsc driver to let it know that the device is being |
| 2274 | * removed. Also blocks mtu and channel changes. | 2290 | * removed. Also blocks mtu and channel changes. |
| 2275 | */ | 2291 | */ |
| 2276 | rtnl_lock(); | ||
| 2277 | vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev); | 2292 | vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev); |
| 2278 | if (vf_netdev) | 2293 | if (vf_netdev) |
| 2279 | netvsc_unregister_vf(vf_netdev); | 2294 | netvsc_unregister_vf(vf_netdev); |
| @@ -2285,7 +2300,6 @@ static int netvsc_remove(struct hv_device *dev) | |||
| 2285 | list_del(&ndev_ctx->list); | 2300 | list_del(&ndev_ctx->list); |
| 2286 | 2301 | ||
| 2287 | rtnl_unlock(); | 2302 | rtnl_unlock(); |
| 2288 | rcu_read_unlock(); | ||
| 2289 | 2303 | ||
| 2290 | hv_set_drvdata(dev, NULL); | 2304 | hv_set_drvdata(dev, NULL); |
| 2291 | 2305 | ||
