Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2016-06-10 10:38:20 +0200
committerTakashi Iwai <tiwai@suse.de>2016-06-10 10:38:20 +0200
commitaaa1045fbe447b37a788a27f50a08a1277d5aa0e (patch)
tree9319a95fb9540b462afeff7e0254691e53004a32
parent378db78a5e0f1d877c46d33faccf8c85ef308ec5 (diff)
parent64cc24d541f42854a5ecf05070e7d6a8909993d4 (diff)
Merge branch 'users/ohering/SLE12-SP2/for-next' into SLE12-SP2
Pull hyperv updates from Olaf Hering.
-rw-r--r--patches.suse/msft-hv-1023-hv_netvsc-Implement-support-for-VF-drivers-on-Hyper-.patch525
-rw-r--r--patches.suse/msft-hv-1024-hv_netvsc-Fix-the-list-processing-for-network-change.patch33
-rw-r--r--patches.suse/msft-hv-1026-Drivers-hv-vmbus-Introduce-functions-for-estimating-.patch145
-rw-r--r--patches.suse/msft-hv-1027-Drivers-hv-vmbus-Use-READ_ONCE-to-read-variables-tha.patch49
-rw-r--r--patches.suse/msft-hv-1029-Drivers-hv-vmbus-Export-the-vmbus_set_event-API.patch51
-rw-r--r--patches.suse/msft-hv-1032-drivers-hv-Lock-access-to-hyperv_mmio-resource-tree.patch (renamed from patches.suse/suse-hv-0015-hv-Lock-access-to-hyperv_mmio-resource-tree.patch)16
-rw-r--r--patches.suse/msft-hv-1033-drivers-hv-Make-a-function-to-free-mmio-regions-thro.patch (renamed from patches.suse/suse-hv-0014-hv-Make-a-function-to-free-mmio-regions-through-vmbu.patch)16
-rw-r--r--patches.suse/msft-hv-1034-drivers-hv-Use-new-vmbus_mmio_free-from-client-drive.patch84
-rw-r--r--patches.suse/msft-hv-1035-drivers-hv-Reverse-order-of-resources-in-hyperv_mmio.patch (renamed from patches.suse/suse-hv-0017-hv-Reverse-order-of-resources-in-hyperv_mmio.patch)14
-rw-r--r--patches.suse/msft-hv-1036-drivers-hv-Track-allocations-of-children-of-hv_vmbus.patch (renamed from patches.suse/suse-hv-0018-hv-Track-allocations-of-children-of-hv_vmbus-in-priv.patch)16
-rw-r--r--patches.suse/msft-hv-1037-drivers-hv-Record-MMIO-range-in-use-by-frame-buffer.patch99
-rw-r--r--patches.suse/msft-hv-1038-drivers-hv-Separate-out-frame-buffer-logic-when-pick.patch123
-rw-r--r--patches.suse/msft-hv-1040-Drivers-hv-kvp-fix-IP-Failover.patch (renamed from patches.suse/suse-hv-0013-Drivers-hv-kvp-fix-IP-Failover.patch)3
-rw-r--r--patches.suse/msft-hv-1041-Drivers-hv-vmbus-handle-various-crash-scenarios.patch (renamed from patches.suse/suse-hv-Drivers-hv-vmbus-handle-various-crash-scenarios.patch)3
-rw-r--r--patches.suse/msft-hv-1042-Drivers-hv-balloon-don-t-crash-when-memory-is-added-.patch (renamed from patches.suse/suse-hv-Drivers-hv-balloon-don-t-crash-when-memory-is-added-.patch)3
-rw-r--r--patches.suse/msft-hv-1043-Drivers-hv-balloon-reset-host_specified_ha_region.patch32
-rw-r--r--patches.suse/msft-hv-1044-tools-hv-lsvmbus-add-pci-pass-through-UUID.patch30
-rw-r--r--patches.suse/msft-hv-1045-PCI-hv-Report-resources-release-after-stopping-the-b.patch (renamed from patches.suse/suse-hv-PCI-hv-Report-resources-release-after-stopping-the-b.patch)3
-rw-r--r--patches.suse/msft-hv-1046-PCI-hv-Add-explicit-barriers-to-config-space-access.patch (renamed from patches.suse/suse-hv-PCI-hv-Add-explicit-barriers-to-config-space-access.patch)3
-rw-r--r--patches.suse/msft-hv-1047-hv_netvsc-move-start_remove-flag-to-net_device_conte.patch123
-rw-r--r--patches.suse/msft-hv-1048-hv_netvsc-use-start_remove-flag-to-protect-netvsc_li.patch89
-rw-r--r--patches.suse/msft-hv-1049-hv_netvsc-untangle-the-pointer-mess.patch785
-rw-r--r--patches.suse/msft-hv-1050-hv_netvsc-get-rid-of-struct-net_device-pointer-in-st.patch457
-rw-r--r--patches.suse/msft-hv-1051-hv_netvsc-synchronize-netvsc_change_mtu-netvsc_set_c.patch53
-rw-r--r--patches.suse/msft-hv-1052-hv_netvsc-set-nvdev-link-after-populating-chn_table.patch98
-rw-r--r--patches.suse/suse-hv-0016-hv-Use-new-vmbus_mmio_free-from-client-drivers.patch38
-rw-r--r--series.conf36
27 files changed, 2846 insertions, 81 deletions
diff --git a/patches.suse/msft-hv-1023-hv_netvsc-Implement-support-for-VF-drivers-on-Hyper-.patch b/patches.suse/msft-hv-1023-hv_netvsc-Implement-support-for-VF-drivers-on-Hyper-.patch
new file mode 100644
index 0000000000..a9186b9cea
--- /dev/null
+++ b/patches.suse/msft-hv-1023-hv_netvsc-Implement-support-for-VF-drivers-on-Hyper-.patch
@@ -0,0 +1,525 @@
+From: KY Srinivasan <kys@microsoft.com>
+Date: Thu, 14 Apr 2016 16:31:54 -0700
+Patch-mainline: v4.7-rc1
+Subject: hv_netvsc: Implement support for VF drivers on Hyper-V
+Git-commit: 84bf9cefb162b197da20a0f4388929f4b5ba5db4
+Reference: fate#320485
+
+Support VF drivers on Hyper-V. On Hyper-V, each VF instance presented to
+the guest has an associated synthetic interface that shares the MAC address
+with the VF instance. Typically these are bonded together to support
+live migration. By default, the host delivers all the incoming packets
+on the synthetic interface. Once the VF is up, we need to explicitly switch
+the data path on the host to divert traffic onto the VF interface. Even after
+switching the data path, broadcast and multicast packets are always delivered
+on the synthetic interface and these will have to be injected back onto the
+VF interface (if VF is up).
+This patch implements the necessary support in netvsc to support Linux
+VF drivers.
+
+Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
+Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Acked-by: <ohering@suse.de>
+---
+ drivers/net/hyperv/hyperv_net.h | 14 ++
+ drivers/net/hyperv/netvsc.c | 29 ++++
+ drivers/net/hyperv/netvsc_drv.c | 312 ++++++++++++++++++++++++++++++++++----
+ drivers/net/hyperv/rndis_filter.c | 6 +
+ 4 files changed, 335 insertions(+), 26 deletions(-)
+
+diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
+index 8b3bd8e..6700a4d 100644
+--- a/drivers/net/hyperv/hyperv_net.h
++++ b/drivers/net/hyperv/hyperv_net.h
+@@ -202,6 +202,8 @@ int rndis_filter_receive(struct hv_device *dev,
+ int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter);
+ int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac);
+
++void netvsc_switch_datapath(struct netvsc_device *nv_dev, bool vf);
++
+ #define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF)
+
+ #define NVSP_PROTOCOL_VERSION_1 2
+@@ -641,6 +643,12 @@ struct netvsc_reconfig {
+ u32 event;
+ };
+
++struct garp_wrk {
++ struct work_struct dwrk;
++ struct net_device *netdev;
++ struct netvsc_device *netvsc_dev;
++};
++
+ /* The context of the netvsc device */
+ struct net_device_context {
+ /* point back to our device context */
+@@ -656,6 +664,7 @@ struct net_device_context {
+
+ struct work_struct work;
+ u32 msg_enable; /* debug level */
++ struct garp_wrk gwrk;
+
+ struct netvsc_stats __percpu *tx_stats;
+ struct netvsc_stats __percpu *rx_stats;
+@@ -730,6 +739,11 @@ struct netvsc_device {
+ u32 vf_alloc;
+ /* Serial number of the VF to team with */
+ u32 vf_serial;
++ atomic_t open_cnt;
++ /* State to manage the associated VF interface. */
++ bool vf_inject;
++ struct net_device *vf_netdev;
++ atomic_t vf_use_cnt;
+ };
+
+ /* NdisInitialize message */
+diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
+index ec313fc..eddce3c 100644
+--- a/drivers/net/hyperv/netvsc.c
++++ b/drivers/net/hyperv/netvsc.c
+@@ -33,6 +33,30 @@
+
+ #include "hyperv_net.h"
+
++/*
++ * Switch the data path from the synthetic interface to the VF
++ * interface.
++ */
++void netvsc_switch_datapath(struct netvsc_device *nv_dev, bool vf)
++{
++ struct nvsp_message *init_pkt = &nv_dev->channel_init_pkt;
++ struct hv_device *dev = nv_dev->dev;
++
++ memset(init_pkt, 0, sizeof(struct nvsp_message));
++ init_pkt->hdr.msg_type = NVSP_MSG4_TYPE_SWITCH_DATA_PATH;
++ if (vf)
++ init_pkt->msg.v4_msg.active_dp.active_datapath =
++ NVSP_DATAPATH_VF;
++ else
++ init_pkt->msg.v4_msg.active_dp.active_datapath =
++ NVSP_DATAPATH_SYNTHETIC;
++
++ vmbus_sendpacket(dev->channel, init_pkt,
++ sizeof(struct nvsp_message),
++ (unsigned long)init_pkt,
++ VM_PKT_DATA_INBAND, 0);
++}
++
+
+ static struct netvsc_device *alloc_net_device(struct hv_device *device)
+ {
+@@ -52,11 +76,16 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device)
+ init_waitqueue_head(&net_device->wait_drain);
+ net_device->start_remove = false;
+ net_device->destroy = false;
++ atomic_set(&net_device->open_cnt, 0);
++ atomic_set(&net_device->vf_use_cnt, 0);
+ net_device->dev = device;
+ net_device->ndev = ndev;
+ net_device->max_pkt = RNDIS_MAX_PKT_DEFAULT;
+ net_device->pkt_align = RNDIS_PKT_ALIGN_DEFAULT;
+
++ net_device->vf_netdev = NULL;
++ net_device->vf_inject = false;
++
+ hv_set_drvdata(device, net_device);
+ return net_device;
+ }
+diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
+index b8121eb..bfdb568a 100644
+--- a/drivers/net/hyperv/netvsc_drv.c
++++ b/drivers/net/hyperv/netvsc_drv.c
+@@ -610,42 +610,24 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,
+ schedule_delayed_work(&ndev_ctx->dwork, 0);
+ }
+
+-/*
+- * netvsc_recv_callback - Callback when we receive a packet from the
+- * "wire" on the specified device.
+- */
+-int netvsc_recv_callback(struct hv_device *device_obj,
++
++static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net,
+ struct hv_netvsc_packet *packet,
+- void **data,
+ struct ndis_tcp_ip_checksum_info *csum_info,
+- struct vmbus_channel *channel,
+- u16 vlan_tci)
++ void *data, u16 vlan_tci)
+ {
+- struct net_device *net;
+- struct net_device_context *net_device_ctx;
+ struct sk_buff *skb;
+- struct netvsc_stats *rx_stats;
+
+- net = ((struct netvsc_device *)hv_get_drvdata(device_obj))->ndev;
+- if (!net || net->reg_state != NETREG_REGISTERED) {
+- return NVSP_STAT_FAIL;
+- }
+- net_device_ctx = netdev_priv(net);
+- rx_stats = this_cpu_ptr(net_device_ctx->rx_stats);
+-
+- /* Allocate a skb - TODO direct I/O to pages? */
+ skb = netdev_alloc_skb_ip_align(net, packet->total_data_buflen);
+- if (unlikely(!skb)) {
+- ++net->stats.rx_dropped;
+- return NVSP_STAT_FAIL;
+- }
++ if (!skb)
++ return skb;
+
+ /*
+ * Copy to skb. This copy is needed here since the memory pointed by
+ * hv_netvsc_packet cannot be deallocated
+ */
+- memcpy(skb_put(skb, packet->total_data_buflen), *data,
+- packet->total_data_buflen);
++ memcpy(skb_put(skb, packet->total_data_buflen), data,
++ packet->total_data_buflen);
+
+ skb->protocol = eth_type_trans(skb, net);
+ if (csum_info) {
+@@ -663,6 +645,75 @@ int netvsc_recv_callback(struct hv_device *device_obj,
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+ vlan_tci);
+
++ return skb;
++}
++
++/*
++ * netvsc_recv_callback - Callback when we receive a packet from the
++ * "wire" on the specified device.
++ */
++int netvsc_recv_callback(struct hv_device *device_obj,
++ struct hv_netvsc_packet *packet,
++ void **data,
++ struct ndis_tcp_ip_checksum_info *csum_info,
++ struct vmbus_channel *channel,
++ u16 vlan_tci)
++{
++ struct net_device *net;
++ struct net_device_context *net_device_ctx;
++ struct sk_buff *skb;
++ struct sk_buff *vf_skb;
++ struct netvsc_stats *rx_stats;
++ struct netvsc_device *netvsc_dev = hv_get_drvdata(device_obj);
++ u32 bytes_recvd = packet->total_data_buflen;
++ int ret = 0;
++
++ net = netvsc_dev->ndev;
++ if (!net || net->reg_state != NETREG_REGISTERED)
++ return NVSP_STAT_FAIL;
++
++ if (READ_ONCE(netvsc_dev->vf_inject)) {
++ atomic_inc(&netvsc_dev->vf_use_cnt);
++ if (!READ_ONCE(netvsc_dev->vf_inject)) {
++ /*
++ * We raced; just move on.
++ */
++ atomic_dec(&netvsc_dev->vf_use_cnt);
++ goto vf_injection_done;
++ }
++
++ /*
++ * Inject this packet into the VF inerface.
++ * On Hyper-V, multicast and brodcast packets
++ * are only delivered on the synthetic interface
++ * (after subjecting these to policy filters on
++ * the host). Deliver these via the VF interface
++ * in the guest.
++ */
++ vf_skb = netvsc_alloc_recv_skb(netvsc_dev->vf_netdev, packet,
++ csum_info, *data, vlan_tci);
++ if (vf_skb != NULL) {
++ ++netvsc_dev->vf_netdev->stats.rx_packets;
++ netvsc_dev->vf_netdev->stats.rx_bytes += bytes_recvd;
++ netif_receive_skb(vf_skb);
++ } else {
++ ++net->stats.rx_dropped;
++ ret = NVSP_STAT_FAIL;
++ }
++ atomic_dec(&netvsc_dev->vf_use_cnt);
++ return ret;
++ }
++
++vf_injection_done:
++ net_device_ctx = netdev_priv(net);
++ rx_stats = this_cpu_ptr(net_device_ctx->rx_stats);
++
++ /* Allocate a skb - TODO direct I/O to pages? */
++ skb = netvsc_alloc_recv_skb(net, packet, csum_info, *data, vlan_tci);
++ if (unlikely(!skb)) {
++ ++net->stats.rx_dropped;
++ return NVSP_STAT_FAIL;
++ }
+ skb_record_rx_queue(skb, channel->
+ offermsg.offer.sub_channel_index);
+
+@@ -1102,6 +1153,175 @@ static void netvsc_free_netdev(struct net_device *netdev)
+ free_netdev(netdev);
+ }
+
++static void netvsc_notify_peers(struct work_struct *wrk)
++{
++ struct garp_wrk *gwrk;
++
++ gwrk = container_of(wrk, struct garp_wrk, dwrk);
++
++ netdev_notify_peers(gwrk->netdev);
++
++ atomic_dec(&gwrk->netvsc_dev->vf_use_cnt);
++}
++
++static struct netvsc_device *get_netvsc_device(char *mac)
++{
++ struct net_device *dev;
++ struct net_device_context *netvsc_ctx = NULL;
++ int rtnl_locked;
++
++ rtnl_locked = rtnl_trylock();
++
++ for_each_netdev(&init_net, dev) {
++ if (memcmp(dev->dev_addr, mac, ETH_ALEN) == 0) {
++ if (dev->netdev_ops != &device_ops)
++ continue;
++ netvsc_ctx = netdev_priv(dev);
++ break;
++ }
++ }
++ if (rtnl_locked)
++ rtnl_unlock();
++
++ if (netvsc_ctx == NULL)
++ return NULL;
++
++ return hv_get_drvdata(netvsc_ctx->device_ctx);
++}
++
++static int netvsc_register_vf(struct net_device *vf_netdev)
++{
++ struct netvsc_device *netvsc_dev;
++ const struct ethtool_ops *eth_ops = vf_netdev->ethtool_ops;
++
++ if (eth_ops == NULL || eth_ops == &ethtool_ops)
++ 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.
++ */
++ netvsc_dev = get_netvsc_device(vf_netdev->dev_addr);
++ if (netvsc_dev == NULL)
++ return NOTIFY_DONE;
++
++ netdev_info(netvsc_dev->ndev, "VF registering: %s\n", vf_netdev->name);
++ /*
++ * Take a reference on the module.
++ */
++ try_module_get(THIS_MODULE);
++ netvsc_dev->vf_netdev = vf_netdev;
++ return NOTIFY_OK;
++}
++
++
++static int netvsc_vf_up(struct net_device *vf_netdev)
++{
++ struct netvsc_device *netvsc_dev;
++ const struct ethtool_ops *eth_ops = vf_netdev->ethtool_ops;
++ struct net_device_context *net_device_ctx;
++
++ if (eth_ops == &ethtool_ops)
++ return NOTIFY_DONE;
++
++ netvsc_dev = get_netvsc_device(vf_netdev->dev_addr);
++
++ if ((netvsc_dev == NULL) || (netvsc_dev->vf_netdev == NULL))
++ return NOTIFY_DONE;
++
++ netdev_info(netvsc_dev->ndev, "VF up: %s\n", vf_netdev->name);
++ net_device_ctx = netdev_priv(netvsc_dev->ndev);
++ netvsc_dev->vf_inject = true;
++
++ /*
++ * Open the device before switching data path.
++ */
++ rndis_filter_open(net_device_ctx->device_ctx);
++
++ /*
++ * notify the host to switch the data path.
++ */
++ netvsc_switch_datapath(netvsc_dev, true);
++ netdev_info(netvsc_dev->ndev, "Data path switched to VF: %s\n",
++ vf_netdev->name);
++
++ netif_carrier_off(netvsc_dev->ndev);
++
++ /*
++ * Now notify peers. We are scheduling work to
++ * notify peers; take a reference to prevent
++ * the VF interface from vanishing.
++ */
++ atomic_inc(&netvsc_dev->vf_use_cnt);
++ net_device_ctx->gwrk.netdev = vf_netdev;
++ net_device_ctx->gwrk.netvsc_dev = netvsc_dev;
++ schedule_work(&net_device_ctx->gwrk.dwrk);
++
++ return NOTIFY_OK;
++}
++
++
++static int netvsc_vf_down(struct net_device *vf_netdev)
++{
++ struct netvsc_device *netvsc_dev;
++ struct net_device_context *net_device_ctx;
++ const struct ethtool_ops *eth_ops = vf_netdev->ethtool_ops;
++
++ if (eth_ops == &ethtool_ops)
++ return NOTIFY_DONE;
++
++ netvsc_dev = get_netvsc_device(vf_netdev->dev_addr);
++
++ if ((netvsc_dev == NULL) || (netvsc_dev->vf_netdev == NULL))
++ return NOTIFY_DONE;
++
++ netdev_info(netvsc_dev->ndev, "VF down: %s\n", vf_netdev->name);
++ net_device_ctx = netdev_priv(netvsc_dev->ndev);
++ netvsc_dev->vf_inject = false;
++ /*
++ * Wait for currently active users to
++ * drain out.
++ */
++
++ while (atomic_read(&netvsc_dev->vf_use_cnt) != 0)
++ udelay(50);
++ netvsc_switch_datapath(netvsc_dev, false);
++ netdev_info(netvsc_dev->ndev, "Data path switched from VF: %s\n",
++ vf_netdev->name);
++ rndis_filter_close(net_device_ctx->device_ctx);
++ netif_carrier_on(netvsc_dev->ndev);
++ /*
++ * Notify peers.
++ */
++ atomic_inc(&netvsc_dev->vf_use_cnt);
++ net_device_ctx->gwrk.netdev = netvsc_dev->ndev;
++ net_device_ctx->gwrk.netvsc_dev = netvsc_dev;
++ schedule_work(&net_device_ctx->gwrk.dwrk);
++
++ return NOTIFY_OK;
++}
++
++
++static int netvsc_unregister_vf(struct net_device *vf_netdev)
++{
++ struct netvsc_device *netvsc_dev;
++ const struct ethtool_ops *eth_ops = vf_netdev->ethtool_ops;
++
++ if (eth_ops == &ethtool_ops)
++ return NOTIFY_DONE;
++
++ netvsc_dev = get_netvsc_device(vf_netdev->dev_addr);
++ if (netvsc_dev == NULL)
++ return NOTIFY_DONE;
++ netdev_info(netvsc_dev->ndev, "VF unregistering: %s\n",
++ vf_netdev->name);
++
++ netvsc_dev->vf_netdev = NULL;
++ module_put(THIS_MODULE);
++ return NOTIFY_OK;
++}
++
+ static int netvsc_probe(struct hv_device *dev,
+ const struct hv_vmbus_device_id *dev_id)
+ {
+@@ -1140,6 +1360,7 @@ static int netvsc_probe(struct hv_device *dev,
+ hv_set_drvdata(dev, net);
+ INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_link_change);
+ INIT_WORK(&net_device_ctx->work, do_set_multicast);
++ INIT_WORK(&net_device_ctx->gwrk.dwrk, netvsc_notify_peers);
+
+ spin_lock_init(&net_device_ctx->lock);
+ INIT_LIST_HEAD(&net_device_ctx->reconfig_events);
+@@ -1235,19 +1456,58 @@ 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);
++
++ switch (event) {
++ case NETDEV_REGISTER:
++ return netvsc_register_vf(event_dev);
++ 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);
++ 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);
+ }
+
+ static int __init netvsc_drv_init(void)
+ {
++ int ret;
++
+ if (ring_size < RING_SIZE_MIN) {
+ ring_size = RING_SIZE_MIN;
+ pr_info("Increased ring_size to %d (min allowed)\n",
+ ring_size);
+ }
+- return vmbus_driver_register(&netvsc_drv);
++ ret = vmbus_driver_register(&netvsc_drv);
++
++ if (ret)
++ return ret;
++
++ register_netdevice_notifier(&netvsc_netdev_notifier);
++ return 0;
+ }
+
+ MODULE_LICENSE("GPL");
+diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
+index c4e1e04..a59cdeb 100644
+--- a/drivers/net/hyperv/rndis_filter.c
++++ b/drivers/net/hyperv/rndis_filter.c
+@@ -1229,6 +1229,9 @@ int rndis_filter_open(struct hv_device *dev)
+ if (!net_device)
+ return -EINVAL;
+
++ if (atomic_inc_return(&net_device->open_cnt) != 1)
++ return 0;
++
+ return rndis_filter_open_device(net_device->extension);
+ }
+
+@@ -1239,5 +1242,8 @@ int rndis_filter_close(struct hv_device *dev)
+ if (!nvdev)
+ return -EINVAL;
+
++ if (atomic_dec_return(&nvdev->open_cnt) != 0)
++ return 0;
++
+ return rndis_filter_close_device(nvdev->extension);
+ }
diff --git a/patches.suse/msft-hv-1024-hv_netvsc-Fix-the-list-processing-for-network-change.patch b/patches.suse/msft-hv-1024-hv_netvsc-Fix-the-list-processing-for-network-change.patch
new file mode 100644
index 0000000000..d40f770233
--- /dev/null
+++ b/patches.suse/msft-hv-1024-hv_netvsc-Fix-the-list-processing-for-network-change.patch
@@ -0,0 +1,33 @@
+From: Haiyang Zhang <haiyangz@microsoft.com>
+Date: Thu, 21 Apr 2016 16:13:01 -0700
+Patch-mainline: v4.7-rc1
+Subject: hv_netvsc: Fix the list processing for network change event
+Git-commit: 15cfd40771e18a4e9b788c64c9db2606f958b93d
+Reference: fate#320485
+
+RNDIS_STATUS_NETWORK_CHANGE event is handled as two "half events" --
+media disconnect & connect. The second half should be added to the list
+head, not to the tail. So all events are processed in normal order.
+
+Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
+Reviewed-by: K. Y. Srinivasan <kys@microsoft.com>
+Reviewed-by: Vitaly Kuznetsov <vkuznets@redhat.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Acked-by: <ohering@suse.de>
+---
+ drivers/net/hyperv/netvsc_drv.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
+index bfdb568a..ba3f3f3 100644
+--- a/drivers/net/hyperv/netvsc_drv.c
++++ b/drivers/net/hyperv/netvsc_drv.c
+@@ -1125,7 +1125,7 @@ static void netvsc_link_change(struct work_struct *w)
+ netif_tx_stop_all_queues(net);
+ event->event = RNDIS_STATUS_MEDIA_CONNECT;
+ spin_lock_irqsave(&ndev_ctx->lock, flags);
+- list_add_tail(&event->list, &ndev_ctx->reconfig_events);
++ list_add(&event->list, &ndev_ctx->reconfig_events);
+ spin_unlock_irqrestore(&ndev_ctx->lock, flags);
+ reschedule = true;
+ }
diff --git a/patches.suse/msft-hv-1026-Drivers-hv-vmbus-Introduce-functions-for-estimating-.patch b/patches.suse/msft-hv-1026-Drivers-hv-vmbus-Introduce-functions-for-estimating-.patch
new file mode 100644
index 0000000000..3fbb53452a
--- /dev/null
+++ b/patches.suse/msft-hv-1026-Drivers-hv-vmbus-Introduce-functions-for-estimating-.patch
@@ -0,0 +1,145 @@
+From: "K. Y. Srinivasan" <kys@microsoft.com>
+Date: Sat, 2 Apr 2016 17:59:46 -0700
+Patch-mainline: v4.7-rc1
+Subject: Drivers: hv: vmbus: Introduce functions for estimating room in the ring buffer
+Git-commit: a6341f000024cdf1ec14dc26743a409a17378db5
+Reference: fate#320485
+
+Introduce separate functions for estimating how much can be read from
+and written to the ring buffer.
+
+Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Acked-by: <ohering@suse.de>
+---
+ drivers/hv/ring_buffer.c | 25 ++++---------------------
+ include/linux/hyperv.h | 27 +++++++++++++++++++++++++++
+ 2 files changed, 31 insertions(+), 21 deletions(-)
+
+diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c
+index a40a73a..544362c 100644
+--- a/drivers/hv/ring_buffer.c
++++ b/drivers/hv/ring_buffer.c
+@@ -38,8 +38,6 @@ void hv_begin_read(struct hv_ring_buffer_info *rbi)
+
+ u32 hv_end_read(struct hv_ring_buffer_info *rbi)
+ {
+- u32 read;
+- u32 write;
+
+ rbi->ring_buffer->interrupt_mask = 0;
+ mb();
+@@ -49,9 +47,7 @@ u32 hv_end_read(struct hv_ring_buffer_info *rbi)
+ * If it is not, we raced and we need to process new
+ * incoming messages.
+ */
+- hv_get_ringbuffer_availbytes(rbi, &read, &write);
+-
+- return read;
++ return hv_get_bytes_to_read(rbi);
+ }
+
+ /*
+@@ -106,9 +102,6 @@ static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi)
+ static bool hv_need_to_signal_on_read(struct hv_ring_buffer_info *rbi)
+ {
+ u32 cur_write_sz;
+- u32 r_size;
+- u32 write_loc;
+- u32 read_loc = rbi->ring_buffer->read_index;
+ u32 pending_sz;
+
+ /*
+@@ -125,14 +118,11 @@ static bool hv_need_to_signal_on_read(struct hv_ring_buffer_info *rbi)
+ mb();
+
+ pending_sz = rbi->ring_buffer->pending_send_sz;
+- write_loc = rbi->ring_buffer->write_index;
+ /* If the other end is not blocked on write don't bother. */
+ if (pending_sz == 0)
+ return false;
+
+- r_size = rbi->ring_datasize;
+- cur_write_sz = write_loc >= read_loc ? r_size - (write_loc - read_loc) :
+- read_loc - write_loc;
++ cur_write_sz = hv_get_bytes_to_write(rbi);
+
+ if (cur_write_sz >= pending_sz)
+ return true;
+@@ -332,7 +322,6 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
+ {
+ int i = 0;
+ u32 bytes_avail_towrite;
+- u32 bytes_avail_toread;
+ u32 totalbytes_towrite = 0;
+
+ u32 next_write_location;
+@@ -348,9 +337,7 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
+ if (lock)
+ spin_lock_irqsave(&outring_info->ring_lock, flags);
+
+- hv_get_ringbuffer_availbytes(outring_info,
+- &bytes_avail_toread,
+- &bytes_avail_towrite);
++ bytes_avail_towrite = hv_get_bytes_to_write(outring_info);
+
+ /*
+ * If there is only room for the packet, assume it is full.
+@@ -401,7 +388,6 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info,
+ void *buffer, u32 buflen, u32 *buffer_actual_len,
+ u64 *requestid, bool *signal, bool raw)
+ {
+- u32 bytes_avail_towrite;
+ u32 bytes_avail_toread;
+ u32 next_read_location = 0;
+ u64 prev_indices = 0;
+@@ -417,10 +403,7 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info,
+ *buffer_actual_len = 0;
+ *requestid = 0;
+
+- hv_get_ringbuffer_availbytes(inring_info,
+- &bytes_avail_toread,
+- &bytes_avail_towrite);
+-
++ bytes_avail_toread = hv_get_bytes_to_read(inring_info);
+ /* Make sure there is something to read */
+ if (bytes_avail_toread < sizeof(desc)) {
+ /*
+diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
+index aa0fadc..66226ce 100644
+--- a/include/linux/hyperv.h
++++ b/include/linux/hyperv.h
+@@ -151,6 +151,33 @@ hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi,
+ *read = dsize - *write;
+ }
+
++static inline u32 hv_get_bytes_to_read(struct hv_ring_buffer_info *rbi)
++{
++ u32 read_loc, write_loc, dsize, read;
++
++ dsize = rbi->ring_datasize;
++ read_loc = rbi->ring_buffer->read_index;
++ write_loc = READ_ONCE(rbi->ring_buffer->write_index);
++
++ read = write_loc >= read_loc ? (write_loc - read_loc) :
++ (dsize - read_loc) + write_loc;
++
++ return read;
++}
++
++static inline u32 hv_get_bytes_to_write(struct hv_ring_buffer_info *rbi)
++{
++ u32 read_loc, write_loc, dsize, write;
++
++ dsize = rbi->ring_datasize;
++ read_loc = READ_ONCE(rbi->ring_buffer->read_index);
++ write_loc = rbi->ring_buffer->write_index;
++
++ write = write_loc >= read_loc ? dsize - (write_loc - read_loc) :
++ read_loc - write_loc;
++ return write;
++}
++
+ /*
+ * VMBUS version is 32 bit entity broken up into
+ * two 16 bit quantities: major_number. minor_number.
diff --git a/patches.suse/msft-hv-1027-Drivers-hv-vmbus-Use-READ_ONCE-to-read-variables-tha.patch b/patches.suse/msft-hv-1027-Drivers-hv-vmbus-Use-READ_ONCE-to-read-variables-tha.patch
new file mode 100644
index 0000000000..fea9f6a9bf
--- /dev/null
+++ b/patches.suse/msft-hv-1027-Drivers-hv-vmbus-Use-READ_ONCE-to-read-variables-tha.patch
@@ -0,0 +1,49 @@
+From: "K. Y. Srinivasan" <kys@microsoft.com>
+Date: Sat, 2 Apr 2016 17:59:47 -0700
+Patch-mainline: v4.7-rc1
+Subject: Drivers: hv: vmbus: Use READ_ONCE() to read variables that are volatile
+Git-commit: d45faaeedb762a3965a0246cf831e55045dd6ef8
+Reference: fate#320485
+
+Use the READ_ONCE macro to access variabes that can change asynchronously.
+This is the recommended mechanism for dealing with "unsafe" compiler
+optimizations.
+
+Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Acked-by: <ohering@suse.de>
+---
+ drivers/hv/ring_buffer.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c
+index 544362c..6ea1b55 100644
+--- a/drivers/hv/ring_buffer.c
++++ b/drivers/hv/ring_buffer.c
+@@ -69,7 +69,7 @@ u32 hv_end_read(struct hv_ring_buffer_info *rbi)
+ static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi)
+ {
+ mb();
+- if (rbi->ring_buffer->interrupt_mask)
++ if (READ_ONCE(rbi->ring_buffer->interrupt_mask))
+ return false;
+
+ /* check interrupt_mask before read_index */
+@@ -78,7 +78,7 @@ static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi)
+ * This is the only case we need to signal when the
+ * ring transitions from being empty to non-empty.
+ */
+- if (old_write == rbi->ring_buffer->read_index)
++ if (old_write == READ_ONCE(rbi->ring_buffer->read_index))
+ return true;
+
+ return false;
+@@ -117,7 +117,7 @@ static bool hv_need_to_signal_on_read(struct hv_ring_buffer_info *rbi)
+ */
+ mb();
+
+- pending_sz = rbi->ring_buffer->pending_send_sz;
++ pending_sz = READ_ONCE(rbi->ring_buffer->pending_send_sz);
+ /* If the other end is not blocked on write don't bother. */
+ if (pending_sz == 0)
+ return false;
diff --git a/patches.suse/msft-hv-1029-Drivers-hv-vmbus-Export-the-vmbus_set_event-API.patch b/patches.suse/msft-hv-1029-Drivers-hv-vmbus-Export-the-vmbus_set_event-API.patch
new file mode 100644
index 0000000000..8cef00f24e
--- /dev/null
+++ b/patches.suse/msft-hv-1029-Drivers-hv-vmbus-Export-the-vmbus_set_event-API.patch
@@ -0,0 +1,51 @@
+From: "K. Y. Srinivasan" <kys@microsoft.com>
+Date: Sat, 2 Apr 2016 17:59:49 -0700
+Patch-mainline: v4.7-rc1
+Subject: Drivers: hv: vmbus: Export the vmbus_set_event() API
+Git-commit: 5cc472477f928fb8584eb8e08245c9cf9002d74a
+Reference: fate#320485
+
+In preparation for moving some ring buffer functionality out of the
+vmbus driver, export the API for signaling the host.
+
+Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Acked-by: <ohering@suse.de>
+---
+ drivers/hv/connection.c | 1 +
+ drivers/hv/hyperv_vmbus.h | 2 --
+ include/linux/hyperv.h | 1 +
+ 3 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
+index d02f137..fcf8a02 100644
+--- a/drivers/hv/connection.c
++++ b/drivers/hv/connection.c
+@@ -495,3 +495,4 @@ void vmbus_set_event(struct vmbus_channel *channel)
+
+ hv_do_hypercall(HVCALL_SIGNAL_EVENT, channel->sig_event, NULL);
+ }
++EXPORT_SYMBOL_GPL(vmbus_set_event);
+diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
+index 12321b9..e5c586f 100644
+--- a/drivers/hv/hyperv_vmbus.h
++++ b/drivers/hv/hyperv_vmbus.h
+@@ -667,8 +667,6 @@ void vmbus_disconnect(void);
+
+ int vmbus_post_msg(void *buffer, size_t buflen);
+
+-void vmbus_set_event(struct vmbus_channel *channel);
+-
+ void vmbus_on_event(unsigned long data);
+ void vmbus_on_msg_dpc(unsigned long data);
+
+diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
+index 66226ce..40fd608 100644
+--- a/include/linux/hyperv.h
++++ b/include/linux/hyperv.h
+@@ -1365,4 +1365,5 @@ extern __u32 vmbus_proto_version;
+
+ int vmbus_send_tl_connect_request(const uuid_le *shv_guest_servie_id,
+ const uuid_le *shv_host_servie_id);
++void vmbus_set_event(struct vmbus_channel *channel);
+ #endif /* _HYPERV_H */
diff --git a/patches.suse/suse-hv-0015-hv-Lock-access-to-hyperv_mmio-resource-tree.patch b/patches.suse/msft-hv-1032-drivers-hv-Lock-access-to-hyperv_mmio-resource-tree.patch
index 6ff141c709..a6e6f5c098 100644
--- a/patches.suse/suse-hv-0015-hv-Lock-access-to-hyperv_mmio-resource-tree.patch
+++ b/patches.suse/msft-hv-1032-drivers-hv-Lock-access-to-hyperv_mmio-resource-tree.patch
@@ -1,7 +1,8 @@
From: Jake Oshins <jakeo@microsoft.com>
-Date: Wed, 9 Mar 2016 13:35:22 -0800
-Subject: hv: Lock access to hyperv_mmio resource tree
-Patch-mainline: Submitted (lkml - http://lkml.iu.edu/hypermail/linux/kernel/1603.1/01827.html)
+Date: Tue, 5 Apr 2016 10:22:50 -0700
+Patch-mainline: v4.7-rc1
+Subject: drivers:hv: Lock access to hyperv_mmio resource tree
+Git-commit: e16dad6bfe1437aaee565f875a6713ca7ce81bdf
Reference: fate#320485
In existing code, this tree of resources is created
@@ -13,16 +14,17 @@ tree which can happen on multiple threads.
Signed-off-by: Jake Oshins <jakeo@microsoft.com>
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: <ohering@suse.de>
---
drivers/hv/vmbus_drv.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
-index aab97ea..b1f0dd0 100644
+index 64713ff..799518b 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
-@@ -103,6 +103,7 @@ static struct notifier_block hyperv_panic_block = {
+@@ -102,6 +102,7 @@ static struct notifier_block hyperv_panic_block = {
};
struct resource *hyperv_mmio;
@@ -30,7 +32,7 @@ index aab97ea..b1f0dd0 100644
static int vmbus_exists(void)
{
-@@ -1172,7 +1173,10 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
+@@ -1132,7 +1133,10 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
resource_size_t range_min, range_max, start, local_min, local_max;
const char *dev_n = dev_name(&device_obj->device);
u32 fb_end = screen_info.lfb_base + (screen_info.lfb_size << 1);
@@ -42,7 +44,7 @@ index aab97ea..b1f0dd0 100644
for (iter = hyperv_mmio; iter; iter = iter->sibling) {
if ((iter->start >= max) || (iter->end <= min))
-@@ -1209,13 +1213,17 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
+@@ -1169,13 +1173,17 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
for (; start + size - 1 <= local_max; start += align) {
*new = request_mem_region_exclusive(start, size,
dev_n);
diff --git a/patches.suse/suse-hv-0014-hv-Make-a-function-to-free-mmio-regions-through-vmbu.patch b/patches.suse/msft-hv-1033-drivers-hv-Make-a-function-to-free-mmio-regions-thro.patch
index d0df1d59bf..d632c59229 100644
--- a/patches.suse/suse-hv-0014-hv-Make-a-function-to-free-mmio-regions-through-vmbu.patch
+++ b/patches.suse/msft-hv-1033-drivers-hv-Make-a-function-to-free-mmio-regions-thro.patch
@@ -1,7 +1,8 @@
From: Jake Oshins <jakeo@microsoft.com>
-Date: Wed, 9 Mar 2016 13:35:21 -0800
-Subject: hv: Make a function to free mmio regions through vmbus
-Patch-mainline: Submitted (lkml - http://lkml.iu.edu/hypermail/linux/kernel/1603.1/01831.html)
+Date: Tue, 5 Apr 2016 10:22:51 -0700
+Patch-mainline: v4.7-rc1
+Subject: drivers:hv: Make a function to free mmio regions through vmbus
+Git-commit: 97fb77dc87582300fa3c141b63699f853576cab1
Reference: fate#320485
This patch introduces a function that reverses everything
@@ -12,6 +13,7 @@ is introduced to wrap those actions.
Signed-off-by: Jake Oshins <jakeo@microsoft.com>
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: <ohering@suse.de>
---
drivers/hv/vmbus_drv.c | 15 +++++++++++++++
@@ -19,10 +21,10 @@ Acked-by: <ohering@suse.de>
2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
-index 6d116a3..aab97ea 100644
+index 799518b..60553c1 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
-@@ -1220,6 +1220,21 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
+@@ -1188,6 +1188,21 @@ exit:
EXPORT_SYMBOL_GPL(vmbus_allocate_mmio);
/**
@@ -45,10 +47,10 @@ index 6d116a3..aab97ea 100644
* @cpu_number: CPU number in Linux terms
*
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
-index fde5aeb..c3d26e1 100644
+index 590fee6..b10954a 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
-@@ -1063,7 +1063,7 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
+@@ -1120,7 +1120,7 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
resource_size_t min, resource_size_t max,
resource_size_t size, resource_size_t align,
bool fb_overlap_ok);
diff --git a/patches.suse/msft-hv-1034-drivers-hv-Use-new-vmbus_mmio_free-from-client-drive.patch b/patches.suse/msft-hv-1034-drivers-hv-Use-new-vmbus_mmio_free-from-client-drive.patch
new file mode 100644
index 0000000000..ac332c2f08
--- /dev/null
+++ b/patches.suse/msft-hv-1034-drivers-hv-Use-new-vmbus_mmio_free-from-client-drive.patch
@@ -0,0 +1,84 @@
+From: Jake Oshins <jakeo@microsoft.com>
+Date: Tue, 5 Apr 2016 10:22:52 -0700
+Patch-mainline: v4.7-rc1
+Subject: drivers:hv: Use new vmbus_mmio_free() from client drivers.
+Git-commit: 696ca5e82c057a272381ae6064d59eb97a578397
+Reference: fate#320485
+
+This patch modifies all the callers of vmbus_mmio_allocate()
+to call vmbus_mmio_free() instead of release_mem_region().
+
+Signed-off-by: Jake Oshins <jakeo@microsoft.com>
+Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Acked-by: <ohering@suse.de>
+---
+ drivers/pci/host/pci-hyperv.c | 14 +++++++-------
+ drivers/video/fbdev/hyperv_fb.c | 4 ++--
+ 2 files changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c
+index ed651ba..f2559b6 100644
+--- a/drivers/pci/host/pci-hyperv.c
++++ b/drivers/pci/host/pci-hyperv.c
+@@ -1795,14 +1795,14 @@ static void hv_pci_free_bridge_windows(struct hv_pcibus_device *hbus)
+
+ if (hbus->low_mmio_space && hbus->low_mmio_res) {
+ hbus->low_mmio_res->flags |= IORESOURCE_BUSY;
+- release_mem_region(hbus->low_mmio_res->start,
+- resource_size(hbus->low_mmio_res));
++ vmbus_free_mmio(hbus->low_mmio_res->start,
++ resource_size(hbus->low_mmio_res));
+ }
+
+ if (hbus->high_mmio_space && hbus->high_mmio_res) {
+ hbus->high_mmio_res->flags |= IORESOURCE_BUSY;
+- release_mem_region(hbus->high_mmio_res->start,
+- resource_size(hbus->high_mmio_res));
++ vmbus_free_mmio(hbus->high_mmio_res->start,
++ resource_size(hbus->high_mmio_res));
+ }
+ }
+
+@@ -1880,8 +1880,8 @@ static int hv_pci_allocate_bridge_windows(struct hv_pcibus_device *hbus)
+
+ release_low_mmio:
+ if (hbus->low_mmio_res) {
+- release_mem_region(hbus->low_mmio_res->start,
+- resource_size(hbus->low_mmio_res));
++ vmbus_free_mmio(hbus->low_mmio_res->start,
++ resource_size(hbus->low_mmio_res));
+ }
+
+ return ret;
+@@ -1924,7 +1924,7 @@ static int hv_allocate_config_window(struct hv_pcibus_device *hbus)
+
+ static void hv_free_config_window(struct hv_pcibus_device *hbus)
+ {
+- release_mem_region(hbus->mem_config->start, PCI_CONFIG_MMIO_LENGTH);
++ vmbus_free_mmio(hbus->mem_config->start, PCI_CONFIG_MMIO_LENGTH);
+ }
+
+ /**
+diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c
+index e2451bd..2fd49b2 100644
+--- a/drivers/video/fbdev/hyperv_fb.c
++++ b/drivers/video/fbdev/hyperv_fb.c
+@@ -743,7 +743,7 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
+ err3:
+ iounmap(fb_virt);
+ err2:
+- release_mem_region(par->mem->start, screen_fb_size);
++ vmbus_free_mmio(par->mem->start, screen_fb_size);
+ par->mem = NULL;
+ err1:
+ if (!gen2vm)
+@@ -758,7 +758,7 @@ static void hvfb_putmem(struct fb_info *info)
+ struct hvfb_par *par = info->par;
+
+ iounmap(info->screen_base);
+- release_mem_region(par->mem->start, screen_fb_size);
++ vmbus_free_mmio(par->mem->start, screen_fb_size);
+ par->mem = NULL;
+ }
+
diff --git a/patches.suse/suse-hv-0017-hv-Reverse-order-of-resources-in-hyperv_mmio.patch b/patches.suse/msft-hv-1035-drivers-hv-Reverse-order-of-resources-in-hyperv_mmio.patch
index 21bad8f3c5..3232201514 100644
--- a/patches.suse/suse-hv-0017-hv-Reverse-order-of-resources-in-hyperv_mmio.patch
+++ b/patches.suse/msft-hv-1035-drivers-hv-Reverse-order-of-resources-in-hyperv_mmio.patch
@@ -1,7 +1,8 @@
From: Jake Oshins <jakeo@microsoft.com>
-Date: Wed, 9 Mar 2016 13:35:24 -0800
-Subject: hv: Reverse order of resources in hyperv_mmio
-Patch-mainline: Submitted (lkml - http://lkml.iu.edu/hypermail/linux/kernel/1603.1/01829.html)
+Date: Tue, 5 Apr 2016 10:22:53 -0700
+Patch-mainline: v4.7-rc1
+Subject: drivers:hv: Reverse order of resources in hyperv_mmio
+Git-commit: 23a0683186b7ca0083bfc76b410497f39a9d0351
Reference: fate#320485
A patch later in this series allocates child nodes
@@ -10,16 +11,17 @@ needs to be sorted in ascending order.
Signed-off-by: Jake Oshins <jakeo@microsoft.com>
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: <ohering@suse.de>
---
drivers/hv/vmbus_drv.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
-index b1f0dd0..f11d5bc 100644
+index 60553c1..1ce47d0 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
-@@ -1089,7 +1089,6 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
+@@ -1049,7 +1049,6 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
new_res->end = end;
/*
@@ -27,7 +29,7 @@ index b1f0dd0..f11d5bc 100644
* If two ranges are adjacent, merge them.
*/
do {
-@@ -1110,7 +1109,7 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
+@@ -1070,7 +1069,7 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
break;
}
diff --git a/patches.suse/suse-hv-0018-hv-Track-allocations-of-children-of-hv_vmbus-in-priv.patch b/patches.suse/msft-hv-1036-drivers-hv-Track-allocations-of-children-of-hv_vmbus.patch
index ca92dbff9c..54db4fa9c7 100644
--- a/patches.suse/suse-hv-0018-hv-Track-allocations-of-children-of-hv_vmbus-in-priv.patch
+++ b/patches.suse/msft-hv-1036-drivers-hv-Track-allocations-of-children-of-hv_vmbus.patch
@@ -1,7 +1,8 @@
From: Jake Oshins <jakeo@microsoft.com>
-Date: Wed, 9 Mar 2016 13:35:25 -0800
-Subject: hv: Track allocations of children of hv_vmbus in private resource tree
-Patch-mainline: Submitted (lkml - http://lkml.iu.edu/hypermail/linux/kernel/1603.1/01829.html)
+Date: Tue, 5 Apr 2016 10:22:54 -0700
+Patch-mainline: v4.7-rc1
+Subject: drivers:hv: Track allocations of children of hv_vmbus in private resource tree
+Git-commit: be000f93e5d71f5d43dd722f8eb110b069f9d8a2
Reference: fate#320485
This patch changes vmbus_allocate_mmio() and vmbus_free_mmio() so
@@ -22,16 +23,17 @@ a workaround.
Signed-off-by: Jake Oshins <jakeo@microsoft.com>
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: <ohering@suse.de>
---
drivers/hv/vmbus_drv.c | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
-index f11d5bc..4b40a75 100644
+index 1ce47d0..dfc6149 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
-@@ -1168,7 +1168,7 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
+@@ -1128,7 +1128,7 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
resource_size_t size, resource_size_t align,
bool fb_overlap_ok)
{
@@ -40,7 +42,7 @@ index f11d5bc..4b40a75 100644
resource_size_t range_min, range_max, start, local_min, local_max;
const char *dev_n = dev_name(&device_obj->device);
u32 fb_end = screen_info.lfb_base + (screen_info.lfb_size << 1);
-@@ -1210,12 +1210,22 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
+@@ -1170,12 +1170,22 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
start = (local_min + align - 1) & ~(align - 1);
for (; start + size - 1 <= local_max; start += align) {
@@ -63,7 +65,7 @@ index f11d5bc..4b40a75 100644
}
}
}
-@@ -1236,7 +1246,17 @@ EXPORT_SYMBOL_GPL(vmbus_allocate_mmio);
+@@ -1196,7 +1206,17 @@ EXPORT_SYMBOL_GPL(vmbus_allocate_mmio);
*/
void vmbus_free_mmio(resource_size_t start, resource_size_t size)
{
diff --git a/patches.suse/msft-hv-1037-drivers-hv-Record-MMIO-range-in-use-by-frame-buffer.patch b/patches.suse/msft-hv-1037-drivers-hv-Record-MMIO-range-in-use-by-frame-buffer.patch
new file mode 100644
index 0000000000..3c2ea03d49
--- /dev/null
+++ b/patches.suse/msft-hv-1037-drivers-hv-Record-MMIO-range-in-use-by-frame-buffer.patch
@@ -0,0 +1,99 @@
+From: Jake Oshins <jakeo@microsoft.com>
+Date: Tue, 5 Apr 2016 10:22:55 -0700
+Patch-mainline: v4.7-rc1
+Subject: drivers:hv: Record MMIO range in use by frame buffer
+Git-commit: 6d146aefbaa5c5dff0c2e9d81e90e5112ded284e
+Reference: fate#320485
+
+Later in the boot sequence, we need to figure out which memory
+ranges can be given out to various paravirtual drivers. The
+hyperv_fb driver should, ideally, be placed right on top of
+the frame buffer, without some other device getting plopped on
+top of this range in the meantime. Recording this now allows
+that to be guaranteed.
+
+Signed-off-by: Jake Oshins <jakeo@microsoft.com>
+Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Acked-by: <ohering@suse.de>
+---
+ drivers/hv/vmbus_drv.c | 37 ++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 36 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
+index dfc6149..eaa5c3b 100644
+--- a/drivers/hv/vmbus_drv.c
++++ b/drivers/hv/vmbus_drv.c
+@@ -41,6 +41,7 @@
+ #include <linux/ptrace.h>
+ #include <linux/screen_info.h>
+ #include <linux/kdebug.h>
++#include <linux/efi.h>
+ #include "hyperv_vmbus.h"
+
+ static struct acpi_device *hv_acpi_dev;
+@@ -101,6 +102,8 @@ static struct notifier_block hyperv_panic_block = {
+ .notifier_call = hyperv_panic_event,
+ };
+
++static const char *fb_mmio_name = "fb_range";
++static struct resource *fb_mmio;
+ struct resource *hyperv_mmio;
+ DEFINE_SEMAPHORE(hyperv_mmio_lock);
+
+@@ -1091,6 +1094,12 @@ static int vmbus_acpi_remove(struct acpi_device *device)
+ struct resource *next_res;
+
+ if (hyperv_mmio) {
++ if (fb_mmio) {
++ __release_region(hyperv_mmio, fb_mmio->start,
++ resource_size(fb_mmio));
++ fb_mmio = NULL;
++ }
++
+ for (cur_res = hyperv_mmio; cur_res; cur_res = next_res) {
+ next_res = cur_res->sibling;
+ kfree(cur_res);
+@@ -1100,6 +1109,30 @@ static int vmbus_acpi_remove(struct acpi_device *device)
+ return 0;
+ }
+
++static void vmbus_reserve_fb(void)
++{
++ int size;
++ /*
++ * Make a claim for the frame buffer in the resource tree under the
++ * first node, which will be the one below 4GB. The length seems to
++ * be underreported, particularly in a Generation 1 VM. So start out
++ * reserving a larger area and make it smaller until it succeeds.
++ */
++
++ if (screen_info.lfb_base) {
++ if (efi_enabled(EFI_BOOT))
++ size = max_t(__u32, screen_info.lfb_size, 0x800000);
++ else
++ size = max_t(__u32, screen_info.lfb_size, 0x4000000);
++
++ for (; !fb_mmio && (size >= 0x100000); size >>= 1) {
++ fb_mmio = __request_region(hyperv_mmio,
++ screen_info.lfb_base, size,
++ fb_mmio_name, 0);
++ }
++ }
++}
++
+ /**
+ * vmbus_allocate_mmio() - Pick a memory-mapped I/O range.
+ * @new: If successful, supplied a pointer to the
+@@ -1261,8 +1294,10 @@ static int vmbus_acpi_add(struct acpi_device *device)
+
+ if (ACPI_FAILURE(result))
+ continue;
+- if (hyperv_mmio)
++ if (hyperv_mmio) {
++ vmbus_reserve_fb();
+ break;
++ }
+ }
+ ret_val = 0;
+
diff --git a/patches.suse/msft-hv-1038-drivers-hv-Separate-out-frame-buffer-logic-when-pick.patch b/patches.suse/msft-hv-1038-drivers-hv-Separate-out-frame-buffer-logic-when-pick.patch
new file mode 100644
index 0000000000..3f6997e953
--- /dev/null
+++ b/patches.suse/msft-hv-1038-drivers-hv-Separate-out-frame-buffer-logic-when-pick.patch
@@ -0,0 +1,123 @@
+From: Jake Oshins <jakeo@microsoft.com>
+Date: Tue, 5 Apr 2016 10:22:56 -0700
+Patch-mainline: v4.7-rc1
+Subject: drivers:hv: Separate out frame buffer logic when picking MMIO range
+Git-commit: ea37a6b8a0b9fbe3f85b4b9da3206c28f1de6f8e
+Reference: fate#320485
+
+Simplify the logic that picks MMIO ranges by pulling out the
+logic related to trying to lay frame buffer claim on top of where
+the firmware placed the frame buffer.
+
+Signed-off-by: Jake Oshins <jakeo@microsoft.com>
+Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Acked-by: <ohering@suse.de>
+---
+ drivers/hv/vmbus_drv.c | 80 ++++++++++++++++++++++----------------------------
+ 1 file changed, 35 insertions(+), 45 deletions(-)
+
+diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
+index eaa5c3b..a29a6c0 100644
+--- a/drivers/hv/vmbus_drv.c
++++ b/drivers/hv/vmbus_drv.c
+@@ -1162,64 +1162,54 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
+ bool fb_overlap_ok)
+ {
+ struct resource *iter, *shadow;
+- resource_size_t range_min, range_max, start, local_min, local_max;
++ resource_size_t range_min, range_max, start;
+ const char *dev_n = dev_name(&device_obj->device);
+- u32 fb_end = screen_info.lfb_base + (screen_info.lfb_size << 1);
+- int i, retval;
++ int retval;
+
+ retval = -ENXIO;
+ down(&hyperv_mmio_lock);
+
++ /*
++ * If overlaps with frame buffers are allowed, then first attempt to
++ * make the allocation from within the reserved region. Because it
++ * is already reserved, no shadow allocation is necessary.
++ */
++ if (fb_overlap_ok && fb_mmio && !(min > fb_mmio->end) &&
++ !(max < fb_mmio->start)) {
++
++ range_min = fb_mmio->start;
++ range_max = fb_mmio->end;
++ start = (range_min + align - 1) & ~(align - 1);
++ for (; start + size - 1 <= range_max; start += align) {
++ *new = request_mem_region_exclusive(start, size, dev_n);
++ if (*new) {
++ retval = 0;
++ goto exit;
++ }
++ }
++ }
++
+ for (iter = hyperv_mmio; iter; iter = iter->sibling) {
+ if ((iter->start >= max) || (iter->end <= min))
+ continue;
+
+ range_min = iter->start;
+ range_max = iter->end;
+-
+- /* If this range overlaps the frame buffer, split it into
+- two tries. */
+- for (i = 0; i < 2; i++) {
+- local_min = range_min;
+- local_max = range_max;
+- if (fb_overlap_ok || (range_min >= fb_end) ||
+- (range_max <= screen_info.lfb_base)) {
+- i++;
+- } else {
+- if ((range_min <= screen_info.lfb_base) &&
+- (range_max >= screen_info.lfb_base)) {
+- /*
+- * The frame buffer is in this window,
+- * so trim this into the part that
+- * preceeds the frame buffer.
+- */
+- local_max = screen_info.lfb_base - 1;
+- range_min = fb_end;
+- } else {
+- range_min = fb_end;
+- continue;
+- }
++ start = (range_min + align - 1) & ~(align - 1);
++ for (; start + size - 1 <= range_max; start += align) {
++ shadow = __request_region(iter, start, size, NULL,
++ IORESOURCE_BUSY);
++ if (!shadow)
++ continue;
++
++ *new = request_mem_region_exclusive(start, size, dev_n);
++ if (*new) {
++ shadow->name = (char *)*new;
++ retval = 0;
++ goto exit;
+ }
+
+- start = (local_min + align - 1) & ~(align - 1);
+- for (; start + size - 1 <= local_max; start += align) {
+- shadow = __request_region(iter, start,
+- size,
+- NULL,
+- IORESOURCE_BUSY);
+- if (!shadow)
+- continue;
+-
+- *new = request_mem_region_exclusive(start, size,
+- dev_n);
+- if (*new) {
+- shadow->name = (char *)*new;
+- retval = 0;
+- goto exit;
+- }
+-
+- __release_region(iter, start, size);
+- }
++ __release_region(iter, start, size);
+ }
+ }
+
diff --git a/patches.suse/suse-hv-0013-Drivers-hv-kvp-fix-IP-Failover.patch b/patches.suse/msft-hv-1040-Drivers-hv-kvp-fix-IP-Failover.patch
index 0c4a379180..03f19f167a 100644
--- a/patches.suse/suse-hv-0013-Drivers-hv-kvp-fix-IP-Failover.patch
+++ b/patches.suse/msft-hv-1040-Drivers-hv-kvp-fix-IP-Failover.patch
@@ -1,7 +1,8 @@
From: Vitaly Kuznetsov <vkuznets@redhat.com>
Date: Sat, 30 Apr 2016 19:21:33 -0700
+Patch-mainline: v4.7-rc1
Subject: Drivers: hv: kvp: fix IP Failover
-Patch-mainline: Submitted (lkml - http://lkml.iu.edu/hypermail/linux/kernel/1603.1/01832.html)
+Git-commit: 4dbfc2e68004c60edab7e8fd26784383dd3ee9bc
Reference: fate#320485
Hyper-V VMs can be replicated to another hosts and there is a feature to
diff --git a/patches.suse/suse-hv-Drivers-hv-vmbus-handle-various-crash-scenarios.patch b/patches.suse/msft-hv-1041-Drivers-hv-vmbus-handle-various-crash-scenarios.patch
index 068396b904..e4a2267946 100644
--- a/patches.suse/suse-hv-Drivers-hv-vmbus-handle-various-crash-scenarios.patch
+++ b/patches.suse/msft-hv-1041-Drivers-hv-vmbus-handle-various-crash-scenarios.patch
@@ -1,7 +1,8 @@
From: Vitaly Kuznetsov <vkuznets@redhat.com>
Date: Sat, 30 Apr 2016 19:21:34 -0700
-Patch-mainline: submitted (lkml - https://lkml.org/lkml/2016/3/22/446)
+Patch-mainline: v4.7-rc1
Subject: Drivers: hv: vmbus: handle various crash scenarios
+Git-commit: cd95aad5579371ac332507fc946008217fc37e6c
Reference: fate#320485
Kdump keeps biting. Turns out CHANNELMSG_UNLOAD_RESPONSE is always
diff --git a/patches.suse/suse-hv-Drivers-hv-balloon-don-t-crash-when-memory-is-added-.patch b/patches.suse/msft-hv-1042-Drivers-hv-balloon-don-t-crash-when-memory-is-added-.patch
index 65c7b35c78..cf4d730062 100644
--- a/patches.suse/suse-hv-Drivers-hv-balloon-don-t-crash-when-memory-is-added-.patch
+++ b/patches.suse/msft-hv-1042-Drivers-hv-balloon-don-t-crash-when-memory-is-added-.patch
@@ -1,7 +1,8 @@
From: Vitaly Kuznetsov <vkuznets@redhat.com>
Date: Sat, 30 Apr 2016 19:21:35 -0700
-Patch-mainline: submitted (lkml - https://lkml.org/lkml/2016/4/5/654)
+Patch-mainline: v4.7-rc1
Subject: Drivers: hv: balloon: don't crash when memory is added in non-sorted order
+Git-commit: 77c0c9735bc0ba5898e637a3a20d6bcb50e3f67d
Reference: fate#320485
When we iterate through all HA regions in handle_pg_range() we have an
diff --git a/patches.suse/msft-hv-1043-Drivers-hv-balloon-reset-host_specified_ha_region.patch b/patches.suse/msft-hv-1043-Drivers-hv-balloon-reset-host_specified_ha_region.patch
new file mode 100644
index 0000000000..414f2843ae
--- /dev/null
+++ b/patches.suse/msft-hv-1043-Drivers-hv-balloon-reset-host_specified_ha_region.patch
@@ -0,0 +1,32 @@
+From: Vitaly Kuznetsov <vkuznets@redhat.com>
+Date: Sat, 30 Apr 2016 19:21:36 -0700
+Patch-mainline: v4.7-rc1
+Subject: Drivers: hv: balloon: reset host_specified_ha_region
+Git-commit: d19a55d6ed5bf0ffe553df2d8bf91d054ddf2d76
+Reference: fate#320485
+
+We set host_specified_ha_region = true on certain request but this is a
+global state which stays 'true' forever. We need to reset it when we
+receive a request where ha_region is not specified. I did not see any
+real issues, the bug was found by code inspection.
+
+Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
+Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Acked-by: <ohering@suse.de>
+---
+ drivers/hv/hv_balloon.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c
+index 43af913..df35fb7 100644
+--- a/drivers/hv/hv_balloon.c
++++ b/drivers/hv/hv_balloon.c
+@@ -1400,6 +1400,7 @@ static void balloon_onchannelcallback(void *context)
+ * This is a normal hot-add request specifying
+ * hot-add memory.
+ */
++ dm->host_specified_ha_region = false;
+ ha_pg_range = &ha_msg->range;
+ dm->ha_wrk.ha_page_range = *ha_pg_range;
+ dm->ha_wrk.ha_region_range.page_range = 0;
diff --git a/patches.suse/msft-hv-1044-tools-hv-lsvmbus-add-pci-pass-through-UUID.patch b/patches.suse/msft-hv-1044-tools-hv-lsvmbus-add-pci-pass-through-UUID.patch
new file mode 100644
index 0000000000..687ff73344
--- /dev/null
+++ b/patches.suse/msft-hv-1044-tools-hv-lsvmbus-add-pci-pass-through-UUID.patch
@@ -0,0 +1,30 @@
+From: Vitaly Kuznetsov <vkuznets@redhat.com>
+Date: Sat, 30 Apr 2016 19:21:37 -0700
+Patch-mainline: v4.7-rc1
+Subject: tools: hv: lsvmbus: add pci pass-through UUID
+Git-commit: 552beb4930ef3889d42a049eb9ba3533b4cbe0f6
+Reference: fate#320485
+
+lsvmbus keeps its own copy of all VMBus UUIDs, add PCIe pass-through
+device there to not report 'Unknown' for such devices.
+
+Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
+Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Acked-by: <ohering@suse.de>
+---
+ tools/hv/lsvmbus | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/tools/hv/lsvmbus b/tools/hv/lsvmbus
+index 162a378..e8fecd6 100644
+--- a/tools/hv/lsvmbus
++++ b/tools/hv/lsvmbus
+@@ -35,6 +35,7 @@ vmbus_dev_dict = {
+ '{ba6163d9-04a1-4d29-b605-72e2ffb1dc7f}' : 'Synthetic SCSI Controller',
+ '{2f9bcc4a-0069-4af3-b76b-6fd0be528cda}' : 'Synthetic fiber channel adapter',
+ '{8c2eaf3d-32a7-4b09-ab99-bd1f1c86b501}' : 'Synthetic RDMA adapter',
++ '{44c4f61d-4444-4400-9d52-802e27ede19f}' : 'PCI Express pass-through',
+ '{276aacf4-ac15-426c-98dd-7521ad3f01fe}' : '[Reserved system device]',
+ '{f8e65716-3cb3-4a06-9a60-1889c5cccab5}' : '[Reserved system device]',
+ '{3375baf4-9e15-4b30-b765-67acb10d607b}' : '[Reserved system device]',
diff --git a/patches.suse/suse-hv-PCI-hv-Report-resources-release-after-stopping-the-b.patch b/patches.suse/msft-hv-1045-PCI-hv-Report-resources-release-after-stopping-the-b.patch
index 428453bdfc..529381eee4 100644
--- a/patches.suse/suse-hv-PCI-hv-Report-resources-release-after-stopping-the-b.patch
+++ b/patches.suse/msft-hv-1045-PCI-hv-Report-resources-release-after-stopping-the-b.patch
@@ -1,7 +1,8 @@
From: Vitaly Kuznetsov <vkuznets@redhat.com>
Date: Fri, 29 Apr 2016 11:39:10 +0200
-Patch-mainline: Submitted (helgaas/pci.git)
+Patch-mainline: v4.7-rc1
Subject: PCI: hv: Report resources release after stopping the bus
+Git-commit: deb22e5c84c884a129d801cf3bfde7411536998d
Reference: fate#320485
Kernel hang is observed when pci-hyperv module is release with device
diff --git a/patches.suse/suse-hv-PCI-hv-Add-explicit-barriers-to-config-space-access.patch b/patches.suse/msft-hv-1046-PCI-hv-Add-explicit-barriers-to-config-space-access.patch
index d43fb183ae..f74511bdda 100644
--- a/patches.suse/suse-hv-PCI-hv-Add-explicit-barriers-to-config-space-access.patch
+++ b/patches.suse/msft-hv-1046-PCI-hv-Add-explicit-barriers-to-config-space-access.patch
@@ -1,7 +1,8 @@
From: Vitaly Kuznetsov <vkuznets@redhat.com>
Date: Tue, 3 May 2016 14:22:00 +0200
-Patch-mainline: Submitted (helgaas/pci.git)
+Patch-mainline: v4.7-rc1
Subject: PCI: hv: Add explicit barriers to config space access
+Git-commit: bdd74440d9e887b1fa648eefa17421def5f5243c
Reference: fate#320485
I'm trying to pass-through Broadcom BCM5720 NIC (Dell device 1f5b) on a
diff --git a/patches.suse/msft-hv-1047-hv_netvsc-move-start_remove-flag-to-net_device_conte.patch b/patches.suse/msft-hv-1047-hv_netvsc-move-start_remove-flag-to-net_device_conte.patch
new file mode 100644
index 0000000000..742286c649
--- /dev/null
+++ b/patches.suse/msft-hv-1047-hv_netvsc-move-start_remove-flag-to-net_device_conte.patch
@@ -0,0 +1,123 @@
+From: Vitaly Kuznetsov <vkuznets@redhat.com>
+Date: Fri, 13 May 2016 13:55:20 +0200
+Patch-mainline: v4.7-rc1
+Subject: hv_netvsc: move start_remove flag to net_device_context
+Git-commit: f580aec4bfd7babe51f086e599400027def08ed8
+Reference: fate#320485
+
+struct netvsc_device is destroyed on mtu change so keeping the
+protection flag there is not a good idea. Move it to struct
+net_device_context which is preserved.
+
+Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Acked-by: <ohering@suse.de>
+---
+ drivers/net/hyperv/hyperv_net.h | 4 +++-
+ drivers/net/hyperv/netvsc.c | 3 +--
+ drivers/net/hyperv/netvsc_drv.c | 12 +++++++++---
+ 3 files changed, 13 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
+index 6700a4d..18e9cc8 100644
+--- a/drivers/net/hyperv/hyperv_net.h
++++ b/drivers/net/hyperv/hyperv_net.h
+@@ -672,6 +672,9 @@ struct net_device_context {
+ /* Ethtool settings */
+ u8 duplex;
+ u32 speed;
++
++ /* the device is going away */
++ bool start_remove;
+ };
+
+ /* Per netvsc device */
+@@ -682,7 +685,6 @@ struct netvsc_device {
+
+ atomic_t num_outstanding_sends;
+ wait_queue_head_t wait_drain;
+- bool start_remove;
+ bool destroy;
+
+ /* Receive buffer allocated by us but manages by NetVSP */
+diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
+index eddce3c..5e2017b 100644
+--- a/drivers/net/hyperv/netvsc.c
++++ b/drivers/net/hyperv/netvsc.c
+@@ -74,7 +74,6 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device)
+ }
+
+ init_waitqueue_head(&net_device->wait_drain);
+- net_device->start_remove = false;
+ net_device->destroy = false;
+ atomic_set(&net_device->open_cnt, 0);
+ atomic_set(&net_device->vf_use_cnt, 0);
+@@ -691,7 +690,7 @@ static void netvsc_send_completion(struct netvsc_device *net_device,
+ wake_up(&net_device->wait_drain);
+
+ if (netif_tx_queue_stopped(netdev_get_tx_queue(ndev, q_idx)) &&
+- !net_device->start_remove &&
++ !net_device->nd_ctx->start_remove &&
+ (hv_ringbuf_avail_percent(&channel->outbound) >
+ RING_AVAIL_PERCENT_HIWATER || queue_sends < 1))
+ netif_tx_wake_queue(netdev_get_tx_queue(
+diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
+index ba3f3f3..b3fa2cd 100644
+--- a/drivers/net/hyperv/netvsc_drv.c
++++ b/drivers/net/hyperv/netvsc_drv.c
+@@ -793,7 +793,7 @@ static int netvsc_set_channels(struct net_device *net,
+ goto out;
+
+ do_set:
+- nvdev->start_remove = true;
++ net_device_ctx->start_remove = true;
+ rndis_filter_device_remove(dev);
+
+ nvdev->num_chn = channels->combined_count;
+@@ -837,6 +837,7 @@ static int netvsc_set_channels(struct net_device *net,
+
+ out:
+ netvsc_open(net);
++ net_device_ctx->start_remove = false;
+
+ return ret;
+
+@@ -927,7 +928,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu)
+
+ num_chn = nvdev->num_chn;
+
+- nvdev->start_remove = true;
++ ndevctx->start_remove = true;
+ rndis_filter_device_remove(hdev);
+
+ ndev->mtu = mtu;
+@@ -943,6 +944,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu)
+
+ out:
+ netvsc_open(ndev);
++ ndevctx->start_remove = false;
+
+ return ret;
+ }
+@@ -1358,6 +1360,9 @@ static int netvsc_probe(struct hv_device *dev,
+ }
+
+ hv_set_drvdata(dev, net);
++
++ net_device_ctx->start_remove = false;
++
+ INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_link_change);
+ INIT_WORK(&net_device_ctx->work, do_set_multicast);
+ INIT_WORK(&net_device_ctx->gwrk.dwrk, netvsc_notify_peers);
+@@ -1419,9 +1424,10 @@ static int netvsc_remove(struct hv_device *dev)
+ return 0;
+ }
+
+- net_device->start_remove = true;
+
+ ndev_ctx = netdev_priv(net);
++ ndev_ctx->start_remove = true;
++
+ cancel_delayed_work_sync(&ndev_ctx->dwork);
+ cancel_work_sync(&ndev_ctx->work);
+
diff --git a/patches.suse/msft-hv-1048-hv_netvsc-use-start_remove-flag-to-protect-netvsc_li.patch b/patches.suse/msft-hv-1048-hv_netvsc-use-start_remove-flag-to-protect-netvsc_li.patch
new file mode 100644
index 0000000000..beb5e3bc03
--- /dev/null
+++ b/patches.suse/msft-hv-1048-hv_netvsc-use-start_remove-flag-to-protect-netvsc_li.patch
@@ -0,0 +1,89 @@
+From: Vitaly Kuznetsov <vkuznets@redhat.com>
+Date: Fri, 13 May 2016 13:55:21 +0200
+Patch-mainline: v4.7-rc1
+Subject: hv_netvsc: use start_remove flag to protect netvsc_link_change()
+Git-commit: 1bdcec8a5f05445752a0639edd603ac09ae6c553
+Reference: fate#320485
+
+netvsc_link_change() can race with netvsc_change_mtu() or
+netvsc_set_channels() as these functions destroy struct netvsc_device and
+rndis filter. Use start_remove flag for syncronization. As
+netvsc_change_mtu()/netvsc_set_channels() are called with rtnl lock held
+we need to take it before checking start_remove value in
+netvsc_link_change().
+
+Reported-by: Haiyang Zhang <haiyangz@microsoft.com>
+Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Acked-by: <ohering@suse.de>
+---
+ drivers/net/hyperv/netvsc_drv.c | 21 +++++++++++++++++----
+ 1 file changed, 17 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
+index b3fa2cd..01de2dc 100644
+--- a/drivers/net/hyperv/netvsc_drv.c
++++ b/drivers/net/hyperv/netvsc_drv.c
+@@ -838,6 +838,8 @@ static int netvsc_set_channels(struct net_device *net,
+ out:
+ netvsc_open(net);
+ net_device_ctx->start_remove = false;
++ /* We may have missed link change notifications */
++ schedule_delayed_work(&net_device_ctx->dwork, 0);
+
+ return ret;
+
+@@ -946,6 +948,9 @@ out:
+ netvsc_open(ndev);
+ ndevctx->start_remove = false;
+
++ /* We may have missed link change notifications */
++ schedule_delayed_work(&ndevctx->dwork, 0);
++
+ return ret;
+ }
+
+@@ -1066,6 +1071,11 @@ static void netvsc_link_change(struct work_struct *w)
+ unsigned long flags, next_reconfig, delay;
+
+ ndev_ctx = container_of(w, struct net_device_context, dwork.work);
++
++ rtnl_lock();
++ if (ndev_ctx->start_remove)
++ goto out_unlock;
++
+ net_device = hv_get_drvdata(ndev_ctx->device_ctx);
+ rdev = net_device->extension;
+ net = net_device->ndev;
+@@ -1079,7 +1089,7 @@ static void netvsc_link_change(struct work_struct *w)
+ delay = next_reconfig - jiffies;
+ delay = delay < LINKCHANGE_INT ? delay : LINKCHANGE_INT;
+ schedule_delayed_work(&ndev_ctx->dwork, delay);
+- return;
++ goto out_unlock;
+ }
+ ndev_ctx->last_reconfig = jiffies;
+
+@@ -1093,9 +1103,7 @@ static void netvsc_link_change(struct work_struct *w)
+ spin_unlock_irqrestore(&ndev_ctx->lock, flags);
+
+ if (!event)
+- return;
+-
+- rtnl_lock();
++ goto out_unlock;
+
+ switch (event->event) {
+ /* Only the following events are possible due to the check in
+@@ -1144,6 +1152,11 @@ static void netvsc_link_change(struct work_struct *w)
+ */
+ if (reschedule)
+ schedule_delayed_work(&ndev_ctx->dwork, LINKCHANGE_INT);
++
++ return;
++
++out_unlock:
++ rtnl_unlock();
+ }
+
+ static void netvsc_free_netdev(struct net_device *netdev)
diff --git a/patches.suse/msft-hv-1049-hv_netvsc-untangle-the-pointer-mess.patch b/patches.suse/msft-hv-1049-hv_netvsc-untangle-the-pointer-mess.patch
new file mode 100644
index 0000000000..2c02641d36
--- /dev/null
+++ b/patches.suse/msft-hv-1049-hv_netvsc-untangle-the-pointer-mess.patch
@@ -0,0 +1,785 @@
+From: Vitaly Kuznetsov <vkuznets@redhat.com>
+Date: Fri, 13 May 2016 13:55:22 +0200
+Patch-mainline: v4.7-rc1
+Subject: hv_netvsc: untangle the pointer mess
+Git-commit: 3d541ac5a92af708d0085925d136f875f3a58d57
+Reference: fate#320485
+
+We have the following structures keeping netvsc adapter state:
+- struct net_device
+- struct net_device_context
+- struct netvsc_device
+- struct rndis_device
+- struct hv_device
+and there are pointers/dependencies between them:
+- struct net_device_context is contained in struct net_device
+- struct hv_device has driver_data pointer which points to
+ 'struct net_device' OR 'struct netvsc_device' depending on driver's
+ state (!).
+- struct net_device_context has a pointer to 'struct hv_device'.
+- struct netvsc_device has pointers to 'struct hv_device' and
+ 'struct net_device_context'.
+- struct rndis_device has a pointer to 'struct netvsc_device'.
+
+Different functions get different structures as parameters and use these
+pointers for traveling. The problem is (in addition to keeping in mind
+this complex graph) that some of these structures (struct netvsc_device
+and struct rndis_device) are being removed and re-created on mtu change
+(as we implement it as re-creation of hyper-v device) so our travel using
+these pointers is dangerous.
+
+Simplify this to a the following:
+- add struct netvsc_device pointer to struct net_device_context (which is
+ a part of struct net_device and thus never disappears)
+- remove struct hv_device and struct net_device_context pointers from
+ struct netvsc_device
+- replace pointer to 'struct netvsc_device' with pointer to
+ 'struct net_device'.
+- always keep 'struct net_device' in hv_device driver_data.
+
+We'll end up with the following 'circular' structure:
+
+net_device:
+ [net_device_context] -> netvsc_device -> rndis_device -> net_device
+ -> hv_device -> net_device
+
+On MTU change we'll be removing the 'netvsc_device -> rndis_device'
+branch and re-creating it making the synchronization easier.
+
+There is one additional redundant pointer left, it is struct net_device
+link in struct netvsc_device, it is going to be removed in a separate
+commit.
+
+Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Acked-by: <ohering@suse.de>
+---
+ drivers/net/hyperv/hyperv_net.h | 10 ++---
+ drivers/net/hyperv/netvsc.c | 82 ++++++++++++++++-----------------------
+ drivers/net/hyperv/netvsc_drv.c | 51 ++++++++++--------------
+ drivers/net/hyperv/rndis_filter.c | 78 +++++++++++++++++++------------------
+ 4 files changed, 99 insertions(+), 122 deletions(-)
+
+diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
+index 18e9cc8..0f38743 100644
+--- a/drivers/net/hyperv/hyperv_net.h
++++ b/drivers/net/hyperv/hyperv_net.h
+@@ -158,7 +158,7 @@ enum rndis_device_state {
+ };
+
+ struct rndis_device {
+- struct netvsc_device *net_dev;
++ struct net_device *ndev;
+
+ enum rndis_device_state state;
+ bool link_state;
+@@ -173,6 +173,7 @@ struct rndis_device {
+
+ /* Interface */
+ struct rndis_message;
++struct netvsc_device;
+ int netvsc_device_add(struct hv_device *device, void *additional_info);
+ int netvsc_device_remove(struct hv_device *device);
+ int netvsc_send(struct hv_device *device,
+@@ -653,6 +654,8 @@ struct garp_wrk {
+ struct net_device_context {
+ /* point back to our device context */
+ struct hv_device *device_ctx;
++ /* netvsc_device */
++ struct netvsc_device *nvdev;
+ /* reconfigure work */
+ struct delayed_work dwork;
+ /* last reconfig time */
+@@ -679,8 +682,6 @@ struct net_device_context {
+
+ /* Per netvsc device */
+ struct netvsc_device {
+- struct hv_device *dev;
+-
+ u32 nvsp_version;
+
+ atomic_t num_outstanding_sends;
+@@ -734,9 +735,6 @@ struct netvsc_device {
+ u32 max_pkt; /* max number of pkt in one send, e.g. 8 */
+ u32 pkt_align; /* alignment bytes, e.g. 8 */
+
+- /* The net device context */
+- struct net_device_context *nd_ctx;
+-
+ /* 1: allocated, serial number is valid. 0: not allocated */
+ u32 vf_alloc;
+ /* Serial number of the VF to team with */
+diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
+index 5e2017b..1cd01ad 100644
+--- a/drivers/net/hyperv/netvsc.c
++++ b/drivers/net/hyperv/netvsc.c
+@@ -40,7 +40,9 @@
+ void netvsc_switch_datapath(struct netvsc_device *nv_dev, bool vf)
+ {
+ struct nvsp_message *init_pkt = &nv_dev->channel_init_pkt;
+- struct hv_device *dev = nv_dev->dev;
++ struct net_device *ndev = nv_dev->ndev;
++ struct net_device_context *net_device_ctx = netdev_priv(ndev);
++ struct hv_device *dev = net_device_ctx->device_ctx;
+
+ memset(init_pkt, 0, sizeof(struct nvsp_message));
+ init_pkt->hdr.msg_type = NVSP_MSG4_TYPE_SWITCH_DATA_PATH;
+@@ -62,6 +64,7 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device)
+ {
+ struct netvsc_device *net_device;
+ struct net_device *ndev = hv_get_drvdata(device);
++ struct net_device_context *net_device_ctx = netdev_priv(ndev);
+
+ net_device = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL);
+ if (!net_device)
+@@ -77,7 +80,6 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device)
+ net_device->destroy = false;
+ atomic_set(&net_device->open_cnt, 0);
+ atomic_set(&net_device->vf_use_cnt, 0);
+- net_device->dev = device;
+ net_device->ndev = ndev;
+ net_device->max_pkt = RNDIS_MAX_PKT_DEFAULT;
+ net_device->pkt_align = RNDIS_PKT_ALIGN_DEFAULT;
+@@ -85,7 +87,8 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device)
+ net_device->vf_netdev = NULL;
+ net_device->vf_inject = false;
+
+- hv_set_drvdata(device, net_device);
++ net_device_ctx->nvdev = net_device;
++
+ return net_device;
+ }
+
+@@ -97,9 +100,10 @@ static void free_netvsc_device(struct netvsc_device *nvdev)
+
+ static struct netvsc_device *get_outbound_net_device(struct hv_device *device)
+ {
+- struct netvsc_device *net_device;
++ struct net_device *ndev = hv_get_drvdata(device);
++ struct net_device_context *net_device_ctx = netdev_priv(ndev);
++ struct netvsc_device *net_device = net_device_ctx->nvdev;
+
+- net_device = hv_get_drvdata(device);
+ if (net_device && net_device->destroy)
+ net_device = NULL;
+
+@@ -108,9 +112,9 @@ static struct netvsc_device *get_outbound_net_device(struct hv_device *device)
+
+ static struct netvsc_device *get_inbound_net_device(struct hv_device *device)
+ {
+- struct netvsc_device *net_device;
+-
+- net_device = hv_get_drvdata(device);
++ struct net_device *ndev = hv_get_drvdata(device);
++ struct net_device_context *net_device_ctx = netdev_priv(ndev);
++ struct netvsc_device *net_device = net_device_ctx->nvdev;
+
+ if (!net_device)
+ goto get_in_err;
+@@ -124,11 +128,13 @@ get_in_err:
+ }
+
+
+-static int netvsc_destroy_buf(struct netvsc_device *net_device)
++static int netvsc_destroy_buf(struct hv_device *device)
+ {
+ struct nvsp_message *revoke_packet;
+ int ret = 0;
+- struct net_device *ndev = net_device->ndev;
++ struct net_device *ndev = hv_get_drvdata(device);
++ struct net_device_context *net_device_ctx = netdev_priv(ndev);
++ struct netvsc_device *net_device = net_device_ctx->nvdev;
+
+ /*
+ * If we got a section count, it means we received a
+@@ -146,7 +152,7 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device)
+ revoke_packet->msg.v1_msg.
+ revoke_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID;
+
+- ret = vmbus_sendpacket(net_device->dev->channel,
++ ret = vmbus_sendpacket(device->channel,
+ revoke_packet,
+ sizeof(struct nvsp_message),
+ (unsigned long)revoke_packet,
+@@ -164,8 +170,8 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device)
+
+ /* Teardown the gpadl on the vsp end */
+ if (net_device->recv_buf_gpadl_handle) {
+- ret = vmbus_teardown_gpadl(net_device->dev->channel,
+- net_device->recv_buf_gpadl_handle);
++ ret = vmbus_teardown_gpadl(device->channel,
++ net_device->recv_buf_gpadl_handle);
+
+ /* If we failed here, we might as well return and have a leak
+ * rather than continue and a bugchk
+@@ -206,7 +212,7 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device)
+ revoke_packet->msg.v1_msg.revoke_send_buf.id =
+ NETVSC_SEND_BUFFER_ID;
+
+- ret = vmbus_sendpacket(net_device->dev->channel,
++ ret = vmbus_sendpacket(device->channel,
+ revoke_packet,
+ sizeof(struct nvsp_message),
+ (unsigned long)revoke_packet,
+@@ -222,7 +228,7 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device)
+ }
+ /* Teardown the gpadl on the vsp end */
+ if (net_device->send_buf_gpadl_handle) {
+- ret = vmbus_teardown_gpadl(net_device->dev->channel,
++ ret = vmbus_teardown_gpadl(device->channel,
+ net_device->send_buf_gpadl_handle);
+
+ /* If we failed here, we might as well return and have a leak
+@@ -434,7 +440,7 @@ static int netvsc_init_buf(struct hv_device *device)
+ goto exit;
+
+ cleanup:
+- netvsc_destroy_buf(net_device);
++ netvsc_destroy_buf(device);
+
+ exit:
+ return ret;
+@@ -565,9 +571,9 @@ cleanup:
+ return ret;
+ }
+
+-static void netvsc_disconnect_vsp(struct netvsc_device *net_device)
++static void netvsc_disconnect_vsp(struct hv_device *device)
+ {
+- netvsc_destroy_buf(net_device);
++ netvsc_destroy_buf(device);
+ }
+
+ /*
+@@ -575,24 +581,13 @@ static void netvsc_disconnect_vsp(struct netvsc_device *net_device)
+ */
+ int netvsc_device_remove(struct hv_device *device)
+ {
+- struct netvsc_device *net_device;
+- unsigned long flags;
+-
+- net_device = hv_get_drvdata(device);
+-
+- netvsc_disconnect_vsp(net_device);
++ struct net_device *ndev = hv_get_drvdata(device);
++ struct net_device_context *net_device_ctx = netdev_priv(ndev);
++ struct netvsc_device *net_device = net_device_ctx->nvdev;
+
+- /*
+- * Since we have already drained, we don't need to busy wait
+- * as was done in final_release_stor_device()
+- * Note that we cannot set the ext pointer to NULL until
+- * we have drained - to drain the outgoing packets, we need to
+- * allow incoming packets.
+- */
++ netvsc_disconnect_vsp(device);
+
+- spin_lock_irqsave(&device->channel->inbound_lock, flags);
+- hv_set_drvdata(device, NULL);
+- spin_unlock_irqrestore(&device->channel->inbound_lock, flags);
++ net_device_ctx->nvdev = NULL;
+
+ /*
+ * At this point, no one should be accessing net_device
+@@ -640,12 +635,11 @@ static void netvsc_send_completion(struct netvsc_device *net_device,
+ {
+ struct nvsp_message *nvsp_packet;
+ struct hv_netvsc_packet *nvsc_packet;
+- struct net_device *ndev;
++ struct net_device *ndev = hv_get_drvdata(device);
++ struct net_device_context *net_device_ctx = netdev_priv(ndev);
+ u32 send_index;
+ struct sk_buff *skb;
+
+- ndev = net_device->ndev;
+-
+ nvsp_packet = (struct nvsp_message *)((unsigned long)packet +
+ (packet->offset8 << 3));
+
+@@ -690,7 +684,7 @@ static void netvsc_send_completion(struct netvsc_device *net_device,
+ wake_up(&net_device->wait_drain);
+
+ if (netif_tx_queue_stopped(netdev_get_tx_queue(ndev, q_idx)) &&
+- !net_device->nd_ctx->start_remove &&
++ !net_device_ctx->start_remove &&
+ (hv_ringbuf_avail_percent(&channel->outbound) >
+ RING_AVAIL_PERCENT_HIWATER || queue_sends < 1))
+ netif_tx_wake_queue(netdev_get_tx_queue(
+@@ -1264,17 +1258,7 @@ int netvsc_device_add(struct hv_device *device, void *additional_info)
+
+ net_device->ring_size = ring_size;
+
+- /*
+- * Coming into this function, struct net_device * is
+- * registered as the driver private data.
+- * In alloc_net_device(), we register struct netvsc_device *
+- * as the driver private data and stash away struct net_device *
+- * in struct netvsc_device *.
+- */
+- ndev = net_device->ndev;
+-
+- /* Add netvsc_device context to netvsc_device */
+- net_device->nd_ctx = netdev_priv(ndev);
++ ndev = hv_get_drvdata(device);
+
+ /* Initialize the NetVSC channel extension */
+ init_completion(&net_device->channel_init_wait);
+diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
+index 01de2dc..a33a1c9 100644
+--- a/drivers/net/hyperv/netvsc_drv.c
++++ b/drivers/net/hyperv/netvsc_drv.c
+@@ -70,7 +70,7 @@ static void do_set_multicast(struct work_struct *w)
+ struct netvsc_device *nvdev;
+ struct rndis_device *rdev;
+
+- nvdev = hv_get_drvdata(ndevctx->device_ctx);
++ nvdev = ndevctx->nvdev;
+ if (nvdev == NULL || nvdev->ndev == NULL)
+ return;
+
+@@ -99,7 +99,7 @@ static int netvsc_open(struct net_device *net)
+ {
+ struct net_device_context *net_device_ctx = netdev_priv(net);
+ struct hv_device *device_obj = net_device_ctx->device_ctx;
+- struct netvsc_device *nvdev;
++ struct netvsc_device *nvdev = net_device_ctx->nvdev;
+ struct rndis_device *rdev;
+ int ret = 0;
+
+@@ -114,7 +114,6 @@ static int netvsc_open(struct net_device *net)
+
+ netif_tx_wake_all_queues(net);
+
+- nvdev = hv_get_drvdata(device_obj);
+ rdev = nvdev->extension;
+ if (!rdev->link_state)
+ netif_carrier_on(net);
+@@ -126,7 +125,7 @@ static int netvsc_close(struct net_device *net)
+ {
+ struct net_device_context *net_device_ctx = netdev_priv(net);
+ struct hv_device *device_obj = net_device_ctx->device_ctx;
+- struct netvsc_device *nvdev = hv_get_drvdata(device_obj);
++ struct netvsc_device *nvdev = net_device_ctx->nvdev;
+ int ret;
+ u32 aread, awrite, i, msec = 10, retry = 0, retry_max = 20;
+ struct vmbus_channel *chn;
+@@ -205,8 +204,7 @@ static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb,
+ void *accel_priv, select_queue_fallback_t fallback)
+ {
+ struct net_device_context *net_device_ctx = netdev_priv(ndev);
+- struct hv_device *hdev = net_device_ctx->device_ctx;
+- struct netvsc_device *nvsc_dev = hv_get_drvdata(hdev);
++ struct netvsc_device *nvsc_dev = net_device_ctx->nvdev;
+ u32 hash;
+ u16 q_idx = 0;
+
+@@ -580,7 +578,6 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,
+ struct rndis_indicate_status *indicate = &resp->msg.indicate_status;
+ struct net_device *net;
+ struct net_device_context *ndev_ctx;
+- struct netvsc_device *net_device;
+ struct netvsc_reconfig *event;
+ unsigned long flags;
+
+@@ -590,8 +587,7 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,
+ indicate->status != RNDIS_STATUS_MEDIA_DISCONNECT)
+ return;
+
+- net_device = hv_get_drvdata(device_obj);
+- net = net_device->ndev;
++ net = hv_get_drvdata(device_obj);
+
+ if (!net || net->reg_state != NETREG_REGISTERED)
+ return;
+@@ -659,16 +655,15 @@ int netvsc_recv_callback(struct hv_device *device_obj,
+ struct vmbus_channel *channel,
+ u16 vlan_tci)
+ {
+- struct net_device *net;
+- struct net_device_context *net_device_ctx;
++ struct net_device *net = hv_get_drvdata(device_obj);
++ struct net_device_context *net_device_ctx = netdev_priv(net);
+ struct sk_buff *skb;
+ struct sk_buff *vf_skb;
+ struct netvsc_stats *rx_stats;
+- struct netvsc_device *netvsc_dev = hv_get_drvdata(device_obj);
++ struct netvsc_device *netvsc_dev = net_device_ctx->nvdev;
+ u32 bytes_recvd = packet->total_data_buflen;
+ int ret = 0;
+
+- net = netvsc_dev->ndev;
+ if (!net || net->reg_state != NETREG_REGISTERED)
+ return NVSP_STAT_FAIL;
+
+@@ -743,8 +738,7 @@ static void netvsc_get_channels(struct net_device *net,
+ struct ethtool_channels *channel)
+ {
+ struct net_device_context *net_device_ctx = netdev_priv(net);
+- struct hv_device *dev = net_device_ctx->device_ctx;
+- struct netvsc_device *nvdev = hv_get_drvdata(dev);
++ struct netvsc_device *nvdev = net_device_ctx->nvdev;
+
+ if (nvdev) {
+ channel->max_combined = nvdev->max_chn;
+@@ -757,7 +751,7 @@ static int netvsc_set_channels(struct net_device *net,
+ {
+ struct net_device_context *net_device_ctx = netdev_priv(net);
+ struct hv_device *dev = net_device_ctx->device_ctx;
+- struct netvsc_device *nvdev = hv_get_drvdata(dev);
++ struct netvsc_device *nvdev = net_device_ctx->nvdev;
+ struct netvsc_device_info device_info;
+ u32 num_chn;
+ u32 max_chn;
+@@ -798,9 +792,6 @@ static int netvsc_set_channels(struct net_device *net,
+
+ nvdev->num_chn = channels->combined_count;
+
+- net_device_ctx->device_ctx = dev;
+- hv_set_drvdata(dev, net);
+-
+ memset(&device_info, 0, sizeof(device_info));
+ device_info.num_chn = nvdev->num_chn; /* passed to RNDIS */
+ device_info.ring_size = ring_size;
+@@ -815,7 +806,7 @@ static int netvsc_set_channels(struct net_device *net,
+ goto recover;
+ }
+
+- nvdev = hv_get_drvdata(dev);
++ nvdev = net_device_ctx->nvdev;
+
+ ret = netif_set_real_num_tx_queues(net, nvdev->num_chn);
+ if (ret) {
+@@ -908,8 +899,8 @@ static int netvsc_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+ static int netvsc_change_mtu(struct net_device *ndev, int mtu)
+ {
+ struct net_device_context *ndevctx = netdev_priv(ndev);
+- struct hv_device *hdev = ndevctx->device_ctx;
+- struct netvsc_device *nvdev = hv_get_drvdata(hdev);
++ struct netvsc_device *nvdev = ndevctx->nvdev;
++ struct hv_device *hdev = ndevctx->device_ctx;
+ struct netvsc_device_info device_info;
+ int limit = ETH_DATA_LEN;
+ u32 num_chn;
+@@ -935,9 +926,6 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu)
+
+ ndev->mtu = mtu;
+
+- ndevctx->device_ctx = hdev;
+- hv_set_drvdata(hdev, ndev);
+-
+ memset(&device_info, 0, sizeof(device_info));
+ device_info.ring_size = ring_size;
+ device_info.num_chn = num_chn;
+@@ -1076,7 +1064,7 @@ static void netvsc_link_change(struct work_struct *w)
+ if (ndev_ctx->start_remove)
+ goto out_unlock;
+
+- net_device = hv_get_drvdata(ndev_ctx->device_ctx);
++ net_device = ndev_ctx->nvdev;
+ rdev = net_device->extension;
+ net = net_device->ndev;
+
+@@ -1201,7 +1189,7 @@ static struct netvsc_device *get_netvsc_device(char *mac)
+ if (netvsc_ctx == NULL)
+ return NULL;
+
+- return hv_get_drvdata(netvsc_ctx->device_ctx);
++ return netvsc_ctx->nvdev;
+ }
+
+ static int netvsc_register_vf(struct net_device *vf_netdev)
+@@ -1407,7 +1395,7 @@ static int netvsc_probe(struct hv_device *dev,
+ }
+ memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN);
+
+- nvdev = hv_get_drvdata(dev);
++ nvdev = net_device_ctx->nvdev;
+ netif_set_real_num_tx_queues(net, nvdev->num_chn);
+ netif_set_real_num_rx_queues(net, nvdev->num_chn);
+
+@@ -1429,8 +1417,7 @@ static int netvsc_remove(struct hv_device *dev)
+ struct net_device_context *ndev_ctx;
+ struct netvsc_device *net_device;
+
+- net_device = hv_get_drvdata(dev);
+- net = net_device->ndev;
++ net = hv_get_drvdata(dev);
+
+ if (net == NULL) {
+ dev_err(&dev->device, "No net device to remove\n");
+@@ -1439,6 +1426,8 @@ static int netvsc_remove(struct hv_device *dev)
+
+
+ ndev_ctx = netdev_priv(net);
++ net_device = ndev_ctx->nvdev;
++
+ ndev_ctx->start_remove = true;
+
+ cancel_delayed_work_sync(&ndev_ctx->dwork);
+@@ -1455,6 +1444,8 @@ static int netvsc_remove(struct hv_device *dev)
+ */
+ rndis_filter_device_remove(dev);
+
++ hv_set_drvdata(dev, NULL);
++
+ netvsc_free_netdev(net);
+ return 0;
+ }
+diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
+index a59cdeb..6caba51 100644
+--- a/drivers/net/hyperv/rndis_filter.c
++++ b/drivers/net/hyperv/rndis_filter.c
+@@ -126,11 +126,7 @@ static void put_rndis_request(struct rndis_device *dev,
+ static void dump_rndis_message(struct hv_device *hv_dev,
+ struct rndis_message *rndis_msg)
+ {
+- struct net_device *netdev;
+- struct netvsc_device *net_device;
+-
+- net_device = hv_get_drvdata(hv_dev);
+- netdev = net_device->ndev;
++ struct net_device *netdev = hv_get_drvdata(hv_dev);
+
+ switch (rndis_msg->ndis_msg_type) {
+ case RNDIS_MSG_PACKET:
+@@ -211,6 +207,7 @@ static int rndis_filter_send_request(struct rndis_device *dev,
+ struct hv_netvsc_packet *packet;
+ struct hv_page_buffer page_buf[2];
+ struct hv_page_buffer *pb = page_buf;
++ struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
+
+ /* Setup the packet to send it */
+ packet = &req->pkt;
+@@ -236,7 +233,7 @@ static int rndis_filter_send_request(struct rndis_device *dev,
+ pb[0].len;
+ }
+
+- ret = netvsc_send(dev->net_dev->dev, packet, NULL, &pb, NULL);
++ ret = netvsc_send(net_device_ctx->device_ctx, packet, NULL, &pb, NULL);
+ return ret;
+ }
+
+@@ -262,9 +259,7 @@ static void rndis_filter_receive_response(struct rndis_device *dev,
+ struct rndis_request *request = NULL;
+ bool found = false;
+ unsigned long flags;
+- struct net_device *ndev;
+-
+- ndev = dev->net_dev->ndev;
++ struct net_device *ndev = dev->ndev;
+
+ spin_lock_irqsave(&dev->request_lock, flags);
+ list_for_each_entry(request, &dev->req_list, list_ent) {
+@@ -355,6 +350,7 @@ static int rndis_filter_receive_data(struct rndis_device *dev,
+ struct ndis_pkt_8021q_info *vlan;
+ struct ndis_tcp_ip_checksum_info *csum_info;
+ u16 vlan_tci = 0;
++ struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
+
+ rndis_pkt = &msg->msg.pkt;
+
+@@ -368,7 +364,7 @@ static int rndis_filter_receive_data(struct rndis_device *dev,
+ * should be the data packet size plus the trailer padding size
+ */
+ if (pkt->total_data_buflen < rndis_pkt->data_len) {
+- netdev_err(dev->net_dev->ndev, "rndis message buffer "
++ netdev_err(dev->ndev, "rndis message buffer "
+ "overflow detected (got %u, min %u)"
+ "...dropping this message!\n",
+ pkt->total_data_buflen, rndis_pkt->data_len);
+@@ -390,7 +386,7 @@ static int rndis_filter_receive_data(struct rndis_device *dev,
+ }
+
+ csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO);
+- return netvsc_recv_callback(dev->net_dev->dev, pkt, data,
++ return netvsc_recv_callback(net_device_ctx->device_ctx, pkt, data,
+ csum_info, channel, vlan_tci);
+ }
+
+@@ -399,10 +395,11 @@ int rndis_filter_receive(struct hv_device *dev,
+ void **data,
+ struct vmbus_channel *channel)
+ {
+- struct netvsc_device *net_dev = hv_get_drvdata(dev);
++ struct net_device *ndev = hv_get_drvdata(dev);
++ struct net_device_context *net_device_ctx = netdev_priv(ndev);
++ struct netvsc_device *net_dev = net_device_ctx->nvdev;
+ struct rndis_device *rndis_dev;
+ struct rndis_message *rndis_msg;
+- struct net_device *ndev;
+ int ret = 0;
+
+ if (!net_dev) {
+@@ -410,8 +407,6 @@ int rndis_filter_receive(struct hv_device *dev,
+ goto exit;
+ }
+
+- ndev = net_dev->ndev;
+-
+ /* Make sure the rndis device state is initialized */
+ if (!net_dev->extension) {
+ netdev_err(ndev, "got rndis message but no rndis device - "
+@@ -430,7 +425,7 @@ int rndis_filter_receive(struct hv_device *dev,
+
+ rndis_msg = *data;
+
+- if (netif_msg_rx_err(net_dev->nd_ctx))
++ if (netif_msg_rx_err(net_device_ctx))
+ dump_rndis_message(dev, rndis_msg);
+
+ switch (rndis_msg->ndis_msg_type) {
+@@ -550,9 +545,10 @@ static int rndis_filter_query_device_mac(struct rndis_device *dev)
+
+ int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac)
+ {
+- struct netvsc_device *nvdev = hv_get_drvdata(hdev);
++ struct net_device *ndev = hv_get_drvdata(hdev);
++ struct net_device_context *net_device_ctx = netdev_priv(ndev);
++ struct netvsc_device *nvdev = net_device_ctx->nvdev;
+ struct rndis_device *rdev = nvdev->extension;
+- struct net_device *ndev = nvdev->ndev;
+ struct rndis_request *request;
+ struct rndis_set_request *set;
+ struct rndis_config_parameter_info *cpi;
+@@ -629,9 +625,10 @@ static int
+ rndis_filter_set_offload_params(struct hv_device *hdev,
+ struct ndis_offload_params *req_offloads)
+ {
+- struct netvsc_device *nvdev = hv_get_drvdata(hdev);
++ struct net_device *ndev = hv_get_drvdata(hdev);
++ struct net_device_context *net_device_ctx = netdev_priv(ndev);
++ struct netvsc_device *nvdev = net_device_ctx->nvdev;
+ struct rndis_device *rdev = nvdev->extension;
+- struct net_device *ndev = nvdev->ndev;
+ struct rndis_request *request;
+ struct rndis_set_request *set;
+ struct ndis_offload_params *offload_params;
+@@ -703,7 +700,7 @@ u8 netvsc_hash_key[HASH_KEYLEN] = {
+
+ static int rndis_filter_set_rss_param(struct rndis_device *rdev, int num_queue)
+ {
+- struct net_device *ndev = rdev->net_dev->ndev;
++ struct net_device *ndev = rdev->ndev;
+ struct rndis_request *request;
+ struct rndis_set_request *set;
+ struct rndis_set_complete *set_complete;
+@@ -799,9 +796,7 @@ int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter)
+ u32 status;
+ int ret;
+ unsigned long t;
+- struct net_device *ndev;
+-
+- ndev = dev->net_dev->ndev;
++ struct net_device *ndev = dev->ndev;
+
+ request = get_rndis_request(dev, RNDIS_MSG_SET,
+ RNDIS_MESSAGE_SIZE(struct rndis_set_request) +
+@@ -856,7 +851,8 @@ static int rndis_filter_init_device(struct rndis_device *dev)
+ u32 status;
+ int ret;
+ unsigned long t;
+- struct netvsc_device *nvdev = dev->net_dev;
++ struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
++ struct netvsc_device *nvdev = net_device_ctx->nvdev;
+
+ request = get_rndis_request(dev, RNDIS_MSG_INIT,
+ RNDIS_MESSAGE_SIZE(struct rndis_initialize_request));
+@@ -879,7 +875,6 @@ static int rndis_filter_init_device(struct rndis_device *dev)
+ goto cleanup;
+ }
+
+-
+ t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
+
+ if (t == 0) {
+@@ -910,8 +905,9 @@ static void rndis_filter_halt_device(struct rndis_device *dev)
+ {
+ struct rndis_request *request;
+ struct rndis_halt_request *halt;
+- struct netvsc_device *nvdev = dev->net_dev;
+- struct hv_device *hdev = nvdev->dev;
++ struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
++ struct netvsc_device *nvdev = net_device_ctx->nvdev;
++ struct hv_device *hdev = net_device_ctx->device_ctx;
+ ulong flags;
+
+ /* Attempt to do a rndis device halt */
+@@ -979,13 +975,14 @@ static int rndis_filter_close_device(struct rndis_device *dev)
+
+ static void netvsc_sc_open(struct vmbus_channel *new_sc)
+ {
+- struct netvsc_device *nvscdev;
++ struct net_device *ndev =
++ hv_get_drvdata(new_sc->primary_channel->device_obj);
++ struct net_device_context *net_device_ctx = netdev_priv(ndev);
++ struct netvsc_device *nvscdev = net_device_ctx->nvdev;
+ u16 chn_index = new_sc->offermsg.offer.sub_channel_index;
+ int ret;
+ unsigned long flags;
+
+- nvscdev = hv_get_drvdata(new_sc->primary_channel->device_obj);
+-
+ if (chn_index >= nvscdev->num_chn)
+ return;
+
+@@ -1010,6 +1007,8 @@ int rndis_filter_device_add(struct hv_device *dev,
+ void *additional_info)
+ {
+ int ret;
++ struct net_device *net = hv_get_drvdata(dev);
++ struct net_device_context *net_device_ctx = netdev_priv(net);
+ struct netvsc_device *net_device;
+ struct rndis_device *rndis_device;
+ struct netvsc_device_info *device_info = additional_info;
+@@ -1040,16 +1039,15 @@ int rndis_filter_device_add(struct hv_device *dev,
+ return ret;
+ }
+
+-
+ /* Initialize the rndis device */
+- net_device = hv_get_drvdata(dev);
++ net_device = net_device_ctx->nvdev;
+ net_device->max_chn = 1;
+ net_device->num_chn = 1;
+
+ spin_lock_init(&net_device->sc_lock);
+
+ net_device->extension = rndis_device;
+- rndis_device->net_dev = net_device;
++ rndis_device->ndev = net;
+
+ /* Send the rndis initialization message */
+ ret = rndis_filter_init_device(rndis_device);
+@@ -1198,7 +1196,9 @@ err_dev_remv:
+
+ void rndis_filter_device_remove(struct hv_device *dev)
+ {
+- struct netvsc_device *net_dev = hv_get_drvdata(dev);
++ struct net_device *ndev = hv_get_drvdata(dev);
++ struct net_device_context *net_device_ctx = netdev_priv(ndev);
++ struct netvsc_device *net_dev = net_device_ctx->nvdev;
+ struct rndis_device *rndis_dev = net_dev->extension;
+ unsigned long t;
+
+@@ -1224,7 +1224,9 @@ void rndis_filter_device_remove(struct hv_device *dev)
+
+ int rndis_filter_open(struct hv_device *dev)
+ {
+- struct netvsc_device *net_device = hv_get_drvdata(dev);
++ struct net_device *ndev = hv_get_drvdata(dev);
++ struct net_device_context *net_device_ctx = netdev_priv(ndev);
++ struct netvsc_device *net_device = net_device_ctx->nvdev;
+
+ if (!net_device)
+ return -EINVAL;
+@@ -1237,7 +1239,9 @@ int rndis_filter_open(struct hv_device *dev)
+
+ int rndis_filter_close(struct hv_device *dev)
+ {
+- struct netvsc_device *nvdev = hv_get_drvdata(dev);
++ struct net_device *ndev = hv_get_drvdata(dev);
++ struct net_device_context *net_device_ctx = netdev_priv(ndev);
++ struct netvsc_device *nvdev = net_device_ctx->nvdev;
+
+ if (!nvdev)
+ return -EINVAL;
diff --git a/patches.suse/msft-hv-1050-hv_netvsc-get-rid-of-struct-net_device-pointer-in-st.patch b/patches.suse/msft-hv-1050-hv_netvsc-get-rid-of-struct-net_device-pointer-in-st.patch
new file mode 100644
index 0000000000..d11dba325f
--- /dev/null
+++ b/patches.suse/msft-hv-1050-hv_netvsc-get-rid-of-struct-net_device-pointer-in-st.patch
@@ -0,0 +1,457 @@
+From: Vitaly Kuznetsov <vkuznets@redhat.com>
+Date: Fri, 13 May 2016 13:55:23 +0200
+Patch-mainline: v4.7-rc1
+Subject: hv_netvsc: get rid of struct net_device pointer in struct netvsc_device
+Git-commit: 0a1275ca5128b84ffffc149960969ed351ae00eb
+Reference: fate#320485
+
+Simplify netvsvc pointer graph by getting rid of the redundant ndev
+pointer. We can always get a pointer to struct net_device from somewhere
+else.
+
+Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Acked-by: <ohering@suse.de>
+---
+ drivers/net/hyperv/hyperv_net.h | 5 +--
+ drivers/net/hyperv/netvsc.c | 36 +++++++---------
+ drivers/net/hyperv/netvsc_drv.c | 91 +++++++++++++++++++++++----------------
+ drivers/net/hyperv/rndis_filter.c | 4 +-
+ 4 files changed, 72 insertions(+), 64 deletions(-)
+
+diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
+index 0f38743..c270c5a 100644
+--- a/drivers/net/hyperv/hyperv_net.h
++++ b/drivers/net/hyperv/hyperv_net.h
+@@ -173,7 +173,6 @@ struct rndis_device {
+
+ /* Interface */
+ struct rndis_message;
+-struct netvsc_device;
+ int netvsc_device_add(struct hv_device *device, void *additional_info);
+ int netvsc_device_remove(struct hv_device *device);
+ int netvsc_send(struct hv_device *device,
+@@ -203,7 +202,7 @@ int rndis_filter_receive(struct hv_device *dev,
+ int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter);
+ int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac);
+
+-void netvsc_switch_datapath(struct netvsc_device *nv_dev, bool vf);
++void netvsc_switch_datapath(struct net_device *nv_dev, bool vf);
+
+ #define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF)
+
+@@ -711,8 +710,6 @@ struct netvsc_device {
+ struct nvsp_message revoke_packet;
+ /* unsigned char HwMacAddr[HW_MACADDR_LEN]; */
+
+- struct net_device *ndev;
+-
+ struct vmbus_channel *chn_table[VRSS_CHANNEL_MAX];
+ u32 send_table[VRSS_SEND_TAB_SIZE];
+ u32 max_chn;
+diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
+index 1cd01ad..96b3c32 100644
+--- a/drivers/net/hyperv/netvsc.c
++++ b/drivers/net/hyperv/netvsc.c
+@@ -37,12 +37,12 @@
+ * Switch the data path from the synthetic interface to the VF
+ * interface.
+ */
+-void netvsc_switch_datapath(struct netvsc_device *nv_dev, bool vf)
++void netvsc_switch_datapath(struct net_device *ndev, bool vf)
+ {
+- struct nvsp_message *init_pkt = &nv_dev->channel_init_pkt;
+- struct net_device *ndev = nv_dev->ndev;
+ struct net_device_context *net_device_ctx = netdev_priv(ndev);
+ struct hv_device *dev = net_device_ctx->device_ctx;
++ struct netvsc_device *nv_dev = net_device_ctx->nvdev;
++ struct nvsp_message *init_pkt = &nv_dev->channel_init_pkt;
+
+ memset(init_pkt, 0, sizeof(struct nvsp_message));
+ init_pkt->hdr.msg_type = NVSP_MSG4_TYPE_SWITCH_DATA_PATH;
+@@ -80,7 +80,6 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device)
+ net_device->destroy = false;
+ atomic_set(&net_device->open_cnt, 0);
+ atomic_set(&net_device->vf_use_cnt, 0);
+- net_device->ndev = ndev;
+ net_device->max_pkt = RNDIS_MAX_PKT_DEFAULT;
+ net_device->pkt_align = RNDIS_PKT_ALIGN_DEFAULT;
+
+@@ -263,7 +262,7 @@ static int netvsc_init_buf(struct hv_device *device)
+ net_device = get_outbound_net_device(device);
+ if (!net_device)
+ return -ENODEV;
+- ndev = net_device->ndev;
++ ndev = hv_get_drvdata(device);
+
+ node = cpu_to_node(device->channel->target_cpu);
+ net_device->recv_buf = vzalloc_node(net_device->recv_buf_size, node);
+@@ -453,6 +452,7 @@ static int negotiate_nvsp_ver(struct hv_device *device,
+ struct nvsp_message *init_packet,
+ u32 nvsp_ver)
+ {
++ struct net_device *ndev = hv_get_drvdata(device);
+ int ret;
+ unsigned long t;
+
+@@ -486,8 +486,7 @@ static int negotiate_nvsp_ver(struct hv_device *device,
+ /* NVSPv2 or later: Send NDIS config */
+ memset(init_packet, 0, sizeof(struct nvsp_message));
+ init_packet->hdr.msg_type = NVSP_MSG2_TYPE_SEND_NDIS_CONFIG;
+- init_packet->msg.v2_msg.send_ndis_config.mtu = net_device->ndev->mtu +
+- ETH_HLEN;
++ init_packet->msg.v2_msg.send_ndis_config.mtu = ndev->mtu + ETH_HLEN;
+ init_packet->msg.v2_msg.send_ndis_config.capability.ieee8021q = 1;
+
+ if (nvsp_ver >= NVSP_PROTOCOL_VERSION_5)
+@@ -507,7 +506,6 @@ static int netvsc_connect_vsp(struct hv_device *device)
+ struct netvsc_device *net_device;
+ struct nvsp_message *init_packet;
+ int ndis_version;
+- struct net_device *ndev;
+ u32 ver_list[] = { NVSP_PROTOCOL_VERSION_1, NVSP_PROTOCOL_VERSION_2,
+ NVSP_PROTOCOL_VERSION_4, NVSP_PROTOCOL_VERSION_5 };
+ int i, num_ver = 4; /* number of different NVSP versions */
+@@ -515,7 +513,6 @@ static int netvsc_connect_vsp(struct hv_device *device)
+ net_device = get_outbound_net_device(device);
+ if (!net_device)
+ return -ENODEV;
+- ndev = net_device->ndev;
+
+ init_packet = &net_device->channel_init_pkt;
+
+@@ -768,6 +765,7 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device,
+ }
+
+ static inline int netvsc_send_pkt(
++ struct hv_device *device,
+ struct hv_netvsc_packet *packet,
+ struct netvsc_device *net_device,
+ struct hv_page_buffer **pb,
+@@ -776,7 +774,7 @@ static inline int netvsc_send_pkt(
+ struct nvsp_message nvmsg;
+ u16 q_idx = packet->q_idx;
+ struct vmbus_channel *out_channel = net_device->chn_table[q_idx];
+- struct net_device *ndev = net_device->ndev;
++ struct net_device *ndev = hv_get_drvdata(device);
+ u64 req_id;
+ int ret;
+ struct hv_page_buffer *pgbuf;
+@@ -971,7 +969,8 @@ int netvsc_send(struct hv_device *device,
+ }
+
+ if (msd_send) {
+- m_ret = netvsc_send_pkt(msd_send, net_device, NULL, msd_skb);
++ m_ret = netvsc_send_pkt(device, msd_send, net_device,
++ NULL, msd_skb);
+
+ if (m_ret != 0) {
+ netvsc_free_send_slot(net_device,
+@@ -982,7 +981,7 @@ int netvsc_send(struct hv_device *device,
+
+ send_now:
+ if (cur_send)
+- ret = netvsc_send_pkt(cur_send, net_device, pb, skb);
++ ret = netvsc_send_pkt(device, cur_send, net_device, pb, skb);
+
+ if (ret != 0 && section_index != NETVSC_INVALID_INDEX)
+ netvsc_free_send_slot(net_device, section_index);
+@@ -998,9 +997,7 @@ static void netvsc_send_recv_completion(struct hv_device *device,
+ struct nvsp_message recvcompMessage;
+ int retries = 0;
+ int ret;
+- struct net_device *ndev;
+-
+- ndev = net_device->ndev;
++ struct net_device *ndev = hv_get_drvdata(device);
+
+ recvcompMessage.hdr.msg_type =
+ NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE;
+@@ -1047,11 +1044,9 @@ static void netvsc_receive(struct netvsc_device *net_device,
+ u32 status = NVSP_STAT_SUCCESS;
+ int i;
+ int count = 0;
+- struct net_device *ndev;
++ struct net_device *ndev = hv_get_drvdata(device);
+ void *data;
+
+- ndev = net_device->ndev;
+-
+ /*
+ * All inbound packets other than send completion should be xfer page
+ * packet
+@@ -1107,14 +1102,13 @@ static void netvsc_send_table(struct hv_device *hdev,
+ struct nvsp_message *nvmsg)
+ {
+ struct netvsc_device *nvscdev;
+- struct net_device *ndev;
++ struct net_device *ndev = hv_get_drvdata(hdev);
+ int i;
+ u32 count, *tab;
+
+ nvscdev = get_outbound_net_device(hdev);
+ if (!nvscdev)
+ return;
+- ndev = nvscdev->ndev;
+
+ count = nvmsg->msg.v5_msg.send_table.count;
+ if (count != VRSS_SEND_TAB_SIZE) {
+@@ -1173,7 +1167,7 @@ void netvsc_channel_cb(void *context)
+ net_device = get_inbound_net_device(device);
+ if (!net_device)
+ return;
+- ndev = net_device->ndev;
++ ndev = hv_get_drvdata(device);
+ buffer = get_per_channel_state(channel);
+
+ do {
+diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
+index a33a1c9..7325d69 100644
+--- a/drivers/net/hyperv/netvsc_drv.c
++++ b/drivers/net/hyperv/netvsc_drv.c
+@@ -67,18 +67,19 @@ static void do_set_multicast(struct work_struct *w)
+ {
+ struct net_device_context *ndevctx =
+ container_of(w, struct net_device_context, work);
+- struct netvsc_device *nvdev;
++ struct hv_device *device_obj = ndevctx->device_ctx;
++ struct net_device *ndev = hv_get_drvdata(device_obj);
++ struct netvsc_device *nvdev = ndevctx->nvdev;
+ struct rndis_device *rdev;
+
+- nvdev = ndevctx->nvdev;
+- if (nvdev == NULL || nvdev->ndev == NULL)
++ if (!nvdev)
+ return;
+
+ rdev = nvdev->extension;
+ if (rdev == NULL)
+ return;
+
+- if (nvdev->ndev->flags & IFF_PROMISC)
++ if (ndev->flags & IFF_PROMISC)
+ rndis_filter_set_packet_filter(rdev,
+ NDIS_PACKET_TYPE_PROMISCUOUS);
+ else
+@@ -1050,23 +1051,22 @@ static const struct net_device_ops device_ops = {
+ */
+ static void netvsc_link_change(struct work_struct *w)
+ {
+- struct net_device_context *ndev_ctx;
+- struct net_device *net;
++ struct net_device_context *ndev_ctx =
++ container_of(w, struct net_device_context, dwork.work);
++ struct hv_device *device_obj = ndev_ctx->device_ctx;
++ struct net_device *net = hv_get_drvdata(device_obj);
+ struct netvsc_device *net_device;
+ struct rndis_device *rdev;
+ struct netvsc_reconfig *event = NULL;
+ bool notify = false, reschedule = false;
+ unsigned long flags, next_reconfig, delay;
+
+- ndev_ctx = container_of(w, struct net_device_context, dwork.work);
+-
+ rtnl_lock();
+ if (ndev_ctx->start_remove)
+ goto out_unlock;
+
+ net_device = ndev_ctx->nvdev;
+ rdev = net_device->extension;
+- net = net_device->ndev;
+
+ next_reconfig = ndev_ctx->last_reconfig + LINKCHANGE_INT;
+ if (time_is_after_jiffies(next_reconfig)) {
+@@ -1167,10 +1167,9 @@ static void netvsc_notify_peers(struct work_struct *wrk)
+ atomic_dec(&gwrk->netvsc_dev->vf_use_cnt);
+ }
+
+-static struct netvsc_device *get_netvsc_device(char *mac)
++static struct net_device *get_netvsc_net_device(char *mac)
+ {
+- struct net_device *dev;
+- struct net_device_context *netvsc_ctx = NULL;
++ struct net_device *dev, *found = NULL;
+ int rtnl_locked;
+
+ rtnl_locked = rtnl_trylock();
+@@ -1179,21 +1178,20 @@ static struct netvsc_device *get_netvsc_device(char *mac)
+ if (memcmp(dev->dev_addr, mac, ETH_ALEN) == 0) {
+ if (dev->netdev_ops != &device_ops)
+ continue;
+- netvsc_ctx = netdev_priv(dev);
++ found = dev;
+ break;
+ }
+ }
+ if (rtnl_locked)
+ rtnl_unlock();
+
+- if (netvsc_ctx == NULL)
+- return NULL;
+-
+- return netvsc_ctx->nvdev;
++ return found;
+ }
+
+ static int netvsc_register_vf(struct net_device *vf_netdev)
+ {
++ struct net_device *ndev;
++ struct net_device_context *net_device_ctx;
+ struct netvsc_device *netvsc_dev;
+ const struct ethtool_ops *eth_ops = vf_netdev->ethtool_ops;
+
+@@ -1205,11 +1203,16 @@ static int netvsc_register_vf(struct net_device *vf_netdev)
+ * associate with the VF interface. If we don't find a matching
+ * synthetic interface, move on.
+ */
+- netvsc_dev = get_netvsc_device(vf_netdev->dev_addr);
++ ndev = get_netvsc_net_device(vf_netdev->dev_addr);
++ if (!ndev)
++ return NOTIFY_DONE;
++
++ net_device_ctx = netdev_priv(ndev);
++ netvsc_dev = net_device_ctx->nvdev;
+ if (netvsc_dev == NULL)
+ return NOTIFY_DONE;
+
+- netdev_info(netvsc_dev->ndev, "VF registering: %s\n", vf_netdev->name);
++ netdev_info(ndev, "VF registering: %s\n", vf_netdev->name);
+ /*
+ * Take a reference on the module.
+ */
+@@ -1221,6 +1224,7 @@ static int netvsc_register_vf(struct net_device *vf_netdev)
+
+ static int netvsc_vf_up(struct net_device *vf_netdev)
+ {
++ struct net_device *ndev;
+ struct netvsc_device *netvsc_dev;
+ const struct ethtool_ops *eth_ops = vf_netdev->ethtool_ops;
+ struct net_device_context *net_device_ctx;
+@@ -1228,13 +1232,17 @@ static int netvsc_vf_up(struct net_device *vf_netdev)
+ if (eth_ops == &ethtool_ops)
+ return NOTIFY_DONE;
+
+- netvsc_dev = get_netvsc_device(vf_netdev->dev_addr);
++ ndev = get_netvsc_net_device(vf_netdev->dev_addr);
++ if (!ndev)
++ return NOTIFY_DONE;
++
++ net_device_ctx = netdev_priv(ndev);
++ netvsc_dev = net_device_ctx->nvdev;
+
+ if ((netvsc_dev == NULL) || (netvsc_dev->vf_netdev == NULL))
+ return NOTIFY_DONE;
+
+- netdev_info(netvsc_dev->ndev, "VF up: %s\n", vf_netdev->name);
+- net_device_ctx = netdev_priv(netvsc_dev->ndev);
++ netdev_info(ndev, "VF up: %s\n", vf_netdev->name);
+ netvsc_dev->vf_inject = true;
+
+ /*
+@@ -1245,11 +1253,10 @@ static int netvsc_vf_up(struct net_device *vf_netdev)
+ /*
+ * notify the host to switch the data path.
+ */
+- netvsc_switch_datapath(netvsc_dev, true);
+- netdev_info(netvsc_dev->ndev, "Data path switched to VF: %s\n",
+- vf_netdev->name);
++ netvsc_switch_datapath(ndev, true);
++ netdev_info(ndev, "Data path switched to VF: %s\n", vf_netdev->name);
+
+- netif_carrier_off(netvsc_dev->ndev);
++ netif_carrier_off(ndev);
+
+ /*
+ * Now notify peers. We are scheduling work to
+@@ -1267,6 +1274,7 @@ static int netvsc_vf_up(struct net_device *vf_netdev)
+
+ static int netvsc_vf_down(struct net_device *vf_netdev)
+ {
++ struct net_device *ndev;
+ struct netvsc_device *netvsc_dev;
+ struct net_device_context *net_device_ctx;
+ const struct ethtool_ops *eth_ops = vf_netdev->ethtool_ops;
+@@ -1274,13 +1282,17 @@ static int netvsc_vf_down(struct net_device *vf_netdev)
+ if (eth_ops == &ethtool_ops)
+ return NOTIFY_DONE;
+
+- netvsc_dev = get_netvsc_device(vf_netdev->dev_addr);
++ ndev = get_netvsc_net_device(vf_netdev->dev_addr);
++ if (!ndev)
++ return NOTIFY_DONE;
++
++ net_device_ctx = netdev_priv(ndev);
++ netvsc_dev = net_device_ctx->nvdev;
+
+ if ((netvsc_dev == NULL) || (netvsc_dev->vf_netdev == NULL))
+ return NOTIFY_DONE;
+
+- netdev_info(netvsc_dev->ndev, "VF down: %s\n", vf_netdev->name);
+- net_device_ctx = netdev_priv(netvsc_dev->ndev);
++ netdev_info(ndev, "VF down: %s\n", vf_netdev->name);
+ netvsc_dev->vf_inject = false;
+ /*
+ * Wait for currently active users to
+@@ -1289,16 +1301,15 @@ static int netvsc_vf_down(struct net_device *vf_netdev)
+
+ while (atomic_read(&netvsc_dev->vf_use_cnt) != 0)
+ udelay(50);
+- netvsc_switch_datapath(netvsc_dev, false);
+- netdev_info(netvsc_dev->ndev, "Data path switched from VF: %s\n",
+- vf_netdev->name);
++ netvsc_switch_datapath(ndev, false);
++ netdev_info(ndev, "Data path switched from VF: %s\n", vf_netdev->name);
+ rndis_filter_close(net_device_ctx->device_ctx);
+- netif_carrier_on(netvsc_dev->ndev);
++ netif_carrier_on(ndev);
+ /*
+ * Notify peers.
+ */
+ atomic_inc(&netvsc_dev->vf_use_cnt);
+- net_device_ctx->gwrk.netdev = netvsc_dev->ndev;
++ net_device_ctx->gwrk.netdev = ndev;
+ net_device_ctx->gwrk.netvsc_dev = netvsc_dev;
+ schedule_work(&net_device_ctx->gwrk.dwrk);
+
+@@ -1308,17 +1319,23 @@ static int netvsc_vf_down(struct net_device *vf_netdev)
+
+ static int netvsc_unregister_vf(struct net_device *vf_netdev)
+ {
++ struct net_device *ndev;
+ struct netvsc_device *netvsc_dev;
+ const struct ethtool_ops *eth_ops = vf_netdev->ethtool_ops;
++ struct net_device_context *net_device_ctx;
+
+ if (eth_ops == &ethtool_ops)
+ return NOTIFY_DONE;
+
+- netvsc_dev = get_netvsc_device(vf_netdev->dev_addr);
++ ndev = get_netvsc_net_device(vf_netdev->dev_addr);
++ if (!ndev)
++ return NOTIFY_DONE;
++
++ net_device_ctx = netdev_priv(ndev);
++ netvsc_dev = net_device_ctx->nvdev;
+ if (netvsc_dev == NULL)
+ return NOTIFY_DONE;
+- netdev_info(netvsc_dev->ndev, "VF unregistering: %s\n",
+- vf_netdev->name);
++ netdev_info(ndev, "VF unregistering: %s\n", vf_netdev->name);
+
+ netvsc_dev->vf_netdev = NULL;
+ module_put(THIS_MODULE);
+diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
+index 6caba51..97c292b 100644
+--- a/drivers/net/hyperv/rndis_filter.c
++++ b/drivers/net/hyperv/rndis_filter.c
+@@ -1061,8 +1061,8 @@ int rndis_filter_device_add(struct hv_device *dev,
+ ret = rndis_filter_query_device(rndis_device,
+ RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
+ &mtu, &size);
+- if (ret == 0 && size == sizeof(u32) && mtu < net_device->ndev->mtu)
+- net_device->ndev->mtu = mtu;
++ if (ret == 0 && size == sizeof(u32) && mtu < net->mtu)
++ net->mtu = mtu;
+
+ /* Get the mac address */
+ ret = rndis_filter_query_device_mac(rndis_device);
diff --git a/patches.suse/msft-hv-1051-hv_netvsc-synchronize-netvsc_change_mtu-netvsc_set_c.patch b/patches.suse/msft-hv-1051-hv_netvsc-synchronize-netvsc_change_mtu-netvsc_set_c.patch
new file mode 100644
index 0000000000..144ea70de1
--- /dev/null
+++ b/patches.suse/msft-hv-1051-hv_netvsc-synchronize-netvsc_change_mtu-netvsc_set_c.patch
@@ -0,0 +1,53 @@
+From: Vitaly Kuznetsov <vkuznets@redhat.com>
+Date: Fri, 13 May 2016 13:55:24 +0200
+Patch-mainline: v4.7-rc1
+Subject: hv_netvsc: synchronize netvsc_change_mtu()/netvsc_set_channels() with netvsc_remove()
+Git-commit: 6da7225f5a95ba68e3c6225c4051182bef30eed4
+Reference: fate#320485
+
+When netvsc device is removed during mtu change or channels setup we get
+into troubles as both paths are trying to remove the device. Synchronize
+them with start_remove flag and rtnl lock.
+
+Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Acked-by: <ohering@suse.de>
+---
+ drivers/net/hyperv/netvsc_drv.c | 9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
+index 7325d69..6a69b5c 100644
+--- a/drivers/net/hyperv/netvsc_drv.c
++++ b/drivers/net/hyperv/netvsc_drv.c
+@@ -759,7 +759,7 @@ static int netvsc_set_channels(struct net_device *net,
+ int ret = 0;
+ bool recovering = false;
+
+- if (!nvdev || nvdev->destroy)
++ if (net_device_ctx->start_remove || !nvdev || nvdev->destroy)
+ return -ENODEV;
+
+ num_chn = nvdev->num_chn;
+@@ -907,7 +907,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu)
+ u32 num_chn;
+ int ret = 0;
+
+- if (nvdev == NULL || nvdev->destroy)
++ if (ndevctx->start_remove || !nvdev || nvdev->destroy)
+ return -ENODEV;
+
+ if (nvdev->nvsp_version >= NVSP_PROTOCOL_VERSION_2)
+@@ -1445,7 +1445,12 @@ static int netvsc_remove(struct hv_device *dev)
+ ndev_ctx = netdev_priv(net);
+ net_device = ndev_ctx->nvdev;
+
++ /* Avoid racing with netvsc_change_mtu()/netvsc_set_channels()
++ * removing the device.
++ */
++ rtnl_lock();
+ ndev_ctx->start_remove = true;
++ rtnl_unlock();
+
+ cancel_delayed_work_sync(&ndev_ctx->dwork);
+ cancel_work_sync(&ndev_ctx->work);
diff --git a/patches.suse/msft-hv-1052-hv_netvsc-set-nvdev-link-after-populating-chn_table.patch b/patches.suse/msft-hv-1052-hv_netvsc-set-nvdev-link-after-populating-chn_table.patch
new file mode 100644
index 0000000000..1179f3d32f
--- /dev/null
+++ b/patches.suse/msft-hv-1052-hv_netvsc-set-nvdev-link-after-populating-chn_table.patch
@@ -0,0 +1,98 @@
+From: Vitaly Kuznetsov <vkuznets@redhat.com>
+Date: Fri, 13 May 2016 13:55:25 +0200
+Patch-mainline: v4.7-rc1
+Subject: hv_netvsc: set nvdev link after populating chn_table
+Git-commit: 88098834827025cc04c15f1b4b0d9bbef3cf55af
+Reference: fate#320485
+
+Crash in netvsc_send() is observed when netvsc device is re-created on
+mtu change/set channels. The crash is caused by dereferencing of NULL
+channel pointer which comes from chn_table. The root cause is a mixture
+of two facts:
+- we set nvdev pointer in net_device_context in alloc_net_device()
+ before we populate chn_table.
+- we populate chn_table[0] only.
+
+The issue could be papered over by checking channel != NULL in
+netvsc_send() but populating the whole chn_table and writing the
+nvdev pointer afterwards seems more appropriate.
+
+Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Acked-by: <ohering@suse.de>
+---
+ drivers/net/hyperv/netvsc.c | 29 ++++++++++++++++++-----------
+ 1 file changed, 18 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
+index 96b3c32..719cb35 100644
+--- a/drivers/net/hyperv/netvsc.c
++++ b/drivers/net/hyperv/netvsc.c
+@@ -60,11 +60,9 @@ void netvsc_switch_datapath(struct net_device *ndev, bool vf)
+ }
+
+
+-static struct netvsc_device *alloc_net_device(struct hv_device *device)
++static struct netvsc_device *alloc_net_device(void)
+ {
+ struct netvsc_device *net_device;
+- struct net_device *ndev = hv_get_drvdata(device);
+- struct net_device_context *net_device_ctx = netdev_priv(ndev);
+
+ net_device = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL);
+ if (!net_device)
+@@ -86,8 +84,6 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device)
+ net_device->vf_netdev = NULL;
+ net_device->vf_inject = false;
+
+- net_device_ctx->nvdev = net_device;
+-
+ return net_device;
+ }
+
+@@ -1240,20 +1236,19 @@ void netvsc_channel_cb(void *context)
+ */
+ int netvsc_device_add(struct hv_device *device, void *additional_info)
+ {
+- int ret = 0;
++ int i, ret = 0;
+ int ring_size =
+ ((struct netvsc_device_info *)additional_info)->ring_size;
+ struct netvsc_device *net_device;
+- struct net_device *ndev;
++ struct net_device *ndev = hv_get_drvdata(device);
++ struct net_device_context *net_device_ctx = netdev_priv(ndev);
+
+- net_device = alloc_net_device(device);
++ net_device = alloc_net_device();
+ if (!net_device)
+ return -ENOMEM;
+
+ net_device->ring_size = ring_size;
+
+- ndev = hv_get_drvdata(device);
+-
+ /* Initialize the NetVSC channel extension */
+ init_completion(&net_device->channel_init_wait);
+
+@@ -1272,7 +1267,19 @@ int netvsc_device_add(struct hv_device *device, void *additional_info)
+ /* Channel is opened */
+ pr_info("hv_netvsc channel opened successfully\n");
+
+- net_device->chn_table[0] = device->channel;
++ /* If we're reopening the device we may have multiple queues, fill the
++ * chn_table with the default channel to use it before subchannels are
++ * opened.
++ */
++ for (i = 0; i < VRSS_CHANNEL_MAX; i++)
++ net_device->chn_table[i] = device->channel;
++
++ /* Writing nvdev pointer unlocks netvsc_send(), make sure chn_table is
++ * populated.
++ */
++ wmb();
++
++ net_device_ctx->nvdev = net_device;
+
+ /* Connect with the NetVsp */
+ ret = netvsc_connect_vsp(device);
diff --git a/patches.suse/suse-hv-0016-hv-Use-new-vmbus_mmio_free-from-client-drivers.patch b/patches.suse/suse-hv-0016-hv-Use-new-vmbus_mmio_free-from-client-drivers.patch
deleted file mode 100644
index 9ebe0baf1e..0000000000
--- a/patches.suse/suse-hv-0016-hv-Use-new-vmbus_mmio_free-from-client-drivers.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-From: Jake Oshins <jakeo@microsoft.com>
-Date: Wed, 9 Mar 2016 13:35:23 -0800
-Subject: hv: Use new vmbus_mmio_free() from client drivers.
-Patch-mainline: Submitted (lkml - http://lkml.iu.edu/hypermail/linux/kernel/1603.1/01830.html)
-Reference: fate#320485
-
-This patch modifies all the callers of vmbus_mmio_allocate()
-to call vmbus_mmio_free() instead of release_mem_region().
-
-Signed-off-by: Jake Oshins <jakeo@microsoft.com>
-Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
-Acked-by: <ohering@suse.de>
----
- drivers/video/fbdev/hyperv_fb.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c
-index d2a3f9f..be0497c 100644
---- a/drivers/video/fbdev/hyperv_fb.c
-+++ b/drivers/video/fbdev/hyperv_fb.c
-@@ -751,7 +751,7 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
- err3:
- iounmap(fb_virt);
- err2:
-- release_mem_region(par->mem->start, screen_fb_size);
-+ vmbus_free_mmio(par->mem->start, screen_fb_size);
- par->mem = NULL;
- err1:
- if (!gen2vm)
-@@ -766,7 +766,7 @@ static void hvfb_putmem(struct fb_info *info)
- struct hvfb_par *par = info->par;
-
- iounmap(info->screen_base);
-- release_mem_region(par->mem->start, screen_fb_size);
-+ vmbus_free_mmio(par->mem->start, screen_fb_size);
- par->mem = NULL;
- }
-
diff --git a/series.conf b/series.conf
index 2a4fbe5cd2..87ebc85873 100644
--- a/series.conf
+++ b/series.conf
@@ -1313,21 +1313,35 @@
patches.suse/msft-hv-1021-hv_netvsc-Fix-the-order-of-num_sc_offered-decrement.patch
patches.suse/msft-hv-1022-x86-hyperv-Avoid-reporting-bogus-NMI-status-for-Gen2.patch
patches.suse/msft-hv-1023-Drivers-hv-vmbus-Fix-signaling-logic-in-hv_need_to_s.patch
+ patches.suse/msft-hv-1023-hv_netvsc-Implement-support-for-VF-drivers-on-Hyper-.patch
+ patches.suse/msft-hv-1024-hv_netvsc-Fix-the-list-processing-for-network-change.patch
+ patches.suse/msft-hv-1026-Drivers-hv-vmbus-Introduce-functions-for-estimating-.patch
+ patches.suse/msft-hv-1027-Drivers-hv-vmbus-Use-READ_ONCE-to-read-variables-tha.patch
+ patches.suse/msft-hv-1029-Drivers-hv-vmbus-Export-the-vmbus_set_event-API.patch
+ patches.suse/msft-hv-1032-drivers-hv-Lock-access-to-hyperv_mmio-resource-tree.patch
+ patches.suse/msft-hv-1033-drivers-hv-Make-a-function-to-free-mmio-regions-thro.patch
+ patches.suse/msft-hv-1034-drivers-hv-Use-new-vmbus_mmio_free-from-client-drive.patch
+ patches.suse/msft-hv-1035-drivers-hv-Reverse-order-of-resources-in-hyperv_mmio.patch
+ patches.suse/msft-hv-1036-drivers-hv-Track-allocations-of-children-of-hv_vmbus.patch
+ patches.suse/msft-hv-1037-drivers-hv-Record-MMIO-range-in-use-by-frame-buffer.patch
+ patches.suse/msft-hv-1038-drivers-hv-Separate-out-frame-buffer-logic-when-pick.patch
+ patches.suse/msft-hv-1040-Drivers-hv-kvp-fix-IP-Failover.patch
+ patches.suse/msft-hv-1041-Drivers-hv-vmbus-handle-various-crash-scenarios.patch
+ patches.suse/msft-hv-1042-Drivers-hv-balloon-don-t-crash-when-memory-is-added-.patch
+ patches.suse/msft-hv-1043-Drivers-hv-balloon-reset-host_specified_ha_region.patch
+ patches.suse/msft-hv-1044-tools-hv-lsvmbus-add-pci-pass-through-UUID.patch
+ patches.suse/msft-hv-1045-PCI-hv-Report-resources-release-after-stopping-the-b.patch
+ patches.suse/msft-hv-1046-PCI-hv-Add-explicit-barriers-to-config-space-access.patch
+ patches.suse/msft-hv-1047-hv_netvsc-move-start_remove-flag-to-net_device_conte.patch
+ patches.suse/msft-hv-1048-hv_netvsc-use-start_remove-flag-to-protect-netvsc_li.patch
+ patches.suse/msft-hv-1049-hv_netvsc-untangle-the-pointer-mess.patch
+ patches.suse/msft-hv-1050-hv_netvsc-get-rid-of-struct-net_device-pointer-in-st.patch
+ patches.suse/msft-hv-1051-hv_netvsc-synchronize-netvsc_change_mtu-netvsc_set_c.patch
+ patches.suse/msft-hv-1052-hv_netvsc-set-nvdev-link-after-populating-chn_table.patch
- patches.suse/suse-hv-Drivers-hv-balloon-don-t-crash-when-memory-is-added-.patch
- patches.suse/suse-hv-Drivers-hv-vmbus-handle-various-crash-scenarios.patch
patches.suse/suse-hv-hyperv_fb-Add-screen-refresh-after-pause-resume-oper.patch
- patches.suse/suse-hv-PCI-hv-Add-explicit-barriers-to-config-space-access.patch
- patches.suse/suse-hv-PCI-hv-Report-resources-release-after-stopping-the-b.patch
- patches.suse/suse-hv-0013-Drivers-hv-kvp-fix-IP-Failover.patch
- patches.suse/suse-hv-0014-hv-Make-a-function-to-free-mmio-regions-through-vmbu.patch
- patches.suse/suse-hv-0015-hv-Lock-access-to-hyperv_mmio-resource-tree.patch
- patches.suse/suse-hv-0016-hv-Use-new-vmbus_mmio_free-from-client-drivers.patch
- patches.suse/suse-hv-0017-hv-Reverse-order-of-resources-in-hyperv_mmio.patch
- patches.suse/suse-hv-0018-hv-Track-allocations-of-children-of-hv_vmbus-in-priv.patch
-
patches.suse/suse-hv-guest-os-id.patch
patches.suse/suse-hv-storvsc-sg_tablesize.patch