| From 365e1ececb2905f94cc10a5817c5b644a32a3ae2 Mon Sep 17 00:00:00 2001 |
| From: Gaurav Kohli <gauravkohli@linux.microsoft.com> |
| Date: Wed, 5 Oct 2022 22:52:59 -0700 |
| Subject: hv_netvsc: Fix race between VF offering and VF association message from host |
| |
| From: Gaurav Kohli <gauravkohli@linux.microsoft.com> |
| |
| commit 365e1ececb2905f94cc10a5817c5b644a32a3ae2 upstream. |
| |
| During vm boot, there might be possibility that vf registration |
| call comes before the vf association from host to vm. |
| |
| And this might break netvsc vf path, To prevent the same block |
| vf registration until vf bind message comes from host. |
| |
| Cc: stable@vger.kernel.org |
| Fixes: 00d7ddba11436 ("hv_netvsc: pair VF based on serial number") |
| Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com> |
| Signed-off-by: Gaurav Kohli <gauravkohli@linux.microsoft.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/net/hyperv/hyperv_net.h | 3 +++ |
| drivers/net/hyperv/netvsc.c | 4 ++++ |
| drivers/net/hyperv/netvsc_drv.c | 20 ++++++++++++++++++++ |
| 3 files changed, 27 insertions(+) |
| |
| --- a/drivers/net/hyperv/hyperv_net.h |
| +++ b/drivers/net/hyperv/hyperv_net.h |
| @@ -954,6 +954,9 @@ struct net_device_context { |
| u32 vf_alloc; |
| /* Serial number of the VF to team with */ |
| u32 vf_serial; |
| + |
| + /* completion variable to confirm vf association */ |
| + struct completion vf_add; |
| }; |
| |
| /* Per channel data */ |
| --- a/drivers/net/hyperv/netvsc.c |
| +++ b/drivers/net/hyperv/netvsc.c |
| @@ -1223,6 +1223,10 @@ static void netvsc_send_vf(struct net_de |
| |
| net_device_ctx->vf_alloc = nvmsg->msg.v4_msg.vf_assoc.allocated; |
| net_device_ctx->vf_serial = nvmsg->msg.v4_msg.vf_assoc.serial; |
| + |
| + if (net_device_ctx->vf_alloc) |
| + complete(&net_device_ctx->vf_add); |
| + |
| netdev_info(ndev, "VF slot %u %s\n", |
| net_device_ctx->vf_serial, |
| net_device_ctx->vf_alloc ? "added" : "removed"); |
| --- a/drivers/net/hyperv/netvsc_drv.c |
| +++ b/drivers/net/hyperv/netvsc_drv.c |
| @@ -2133,6 +2133,7 @@ static struct net_device *get_netvsc_bys |
| { |
| struct device *parent = vf_netdev->dev.parent; |
| struct net_device_context *ndev_ctx; |
| + struct net_device *ndev; |
| struct pci_dev *pdev; |
| u32 serial; |
| |
| @@ -2159,6 +2160,18 @@ static struct net_device *get_netvsc_bys |
| return hv_get_drvdata(ndev_ctx->device_ctx); |
| } |
| |
| + /* Fallback path to check synthetic vf with |
| + * help of mac addr |
| + */ |
| + list_for_each_entry(ndev_ctx, &netvsc_dev_list, list) { |
| + ndev = hv_get_drvdata(ndev_ctx->device_ctx); |
| + if (ether_addr_equal(vf_netdev->perm_addr, ndev->perm_addr)) { |
| + netdev_notice(vf_netdev, |
| + "falling back to mac addr based matching\n"); |
| + return ndev; |
| + } |
| + } |
| + |
| netdev_notice(vf_netdev, |
| "no netdev found for vf serial:%u\n", serial); |
| return NULL; |
| @@ -2232,6 +2245,11 @@ static int netvsc_vf_changed(struct net_ |
| if (!netvsc_dev) |
| return NOTIFY_DONE; |
| |
| + if (vf_is_up && !net_device_ctx->vf_alloc) { |
| + netdev_info(ndev, "Waiting for the VF association from host\n"); |
| + wait_for_completion(&net_device_ctx->vf_add); |
| + } |
| + |
| 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); |
| @@ -2253,6 +2271,7 @@ static int netvsc_unregister_vf(struct n |
| |
| netdev_info(ndev, "VF unregistering: %s\n", vf_netdev->name); |
| |
| + reinit_completion(&net_device_ctx->vf_add); |
| netdev_rx_handler_unregister(vf_netdev); |
| netdev_upper_dev_unlink(vf_netdev, ndev); |
| RCU_INIT_POINTER(net_device_ctx->vf_netdev, NULL); |
| @@ -2290,6 +2309,7 @@ static int netvsc_probe(struct hv_device |
| |
| INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_link_change); |
| |
| + init_completion(&net_device_ctx->vf_add); |
| spin_lock_init(&net_device_ctx->lock); |
| INIT_LIST_HEAD(&net_device_ctx->reconfig_events); |
| INIT_DELAYED_WORK(&net_device_ctx->vf_takeover, netvsc_vf_setup); |