Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKernel Build Daemon <kbuild@suse.de>2019-04-25 07:12:42 +0200
committerKernel Build Daemon <kbuild@suse.de>2019-04-25 07:12:42 +0200
commitdffcab933a2d0cff3e85ffb73be8ec4c032e2c5e (patch)
tree4a877d4a2c01e3ab63d78f3c8d7a35a5059baa75
parentf320850e4478884d92961355e1eec321c17da7cb (diff)
parenta7b734fe8735cff77cec242dc868885891c2f4bd (diff)
Merge branch 'SLE12-SP3' into openSUSE-42.3
-rw-r--r--patches.suse/floppy-check_events-callback-should-not-return-a-neg.patch36
-rw-r--r--patches.suse/msft-hv-1473-vmbus-add-per-channel-sysfs-info.patch2
-rw-r--r--patches.suse/msft-hv-1507-Drivers-hv-vmbus-Fix-bugs-in-rescind-handling.patch196
-rw-r--r--patches.suse/msft-hv-1721-Drivers-hv-vmbus-Fix-the-offer_in_progress-in-vmbus_.patch39
-rw-r--r--patches.suse/msft-hv-1742-Drivers-hv-vmbus-Reset-the-channel-callback-in-vmbus.patch135
-rw-r--r--patches.suse/msft-hv-1803-Drivers-hv-vmbus-Offload-the-handling-of-channels-to.patch465
-rw-r--r--series.conf5
7 files changed, 877 insertions, 1 deletions
diff --git a/patches.suse/floppy-check_events-callback-should-not-return-a-neg.patch b/patches.suse/floppy-check_events-callback-should-not-return-a-neg.patch
new file mode 100644
index 0000000000..6b1b769943
--- /dev/null
+++ b/patches.suse/floppy-check_events-callback-should-not-return-a-neg.patch
@@ -0,0 +1,36 @@
+From: Yufen Yu <yuyufen@huawei.com>
+Date: Tue, 29 Jan 2019 16:34:04 +0800
+Subject: floppy: check_events callback should not return a negative number
+Git-commit: 96d7cb932e826219ec41ac02e5af037ffae6098c
+Patch-mainline: v5.0-rc7
+References: git-fixes
+
+floppy_check_events() is supposed to return bit flags to say which
+events occured. We should return zero to say that no event flags are
+set. Only BIT(0) and BIT(1) are used in the caller. And .check_events
+interface also expect to return an unsigned int value.
+
+However, after commit a0c80efe5956, it may return -EINTR (-4u).
+Here, both BIT(0) and BIT(1) are cleared. So this patch shouldn't
+affect runtime, but it obviously is still worth fixing.
+
+Reviewed-by: Dan Carpenter <dan.carpenter@oracle.com>
+Fixes: a0c80efe5956 ("floppy: fix lock_fdc() signal handling")
+Signed-off-by: Yufen Yu <yuyufen@huawei.com>
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Jiri Slaby <jslaby@suse.cz>
+---
+ drivers/block/floppy.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/block/floppy.c
++++ b/drivers/block/floppy.c
+@@ -3755,7 +3755,7 @@ static unsigned int floppy_check_events(
+
+ if (time_after(jiffies, UDRS->last_checked + UDP->checkfreq)) {
+ if (lock_fdc(drive))
+- return -EINTR;
++ return 0;
+ poll_drive(false, 0);
+ process_fd_request();
+ }
diff --git a/patches.suse/msft-hv-1473-vmbus-add-per-channel-sysfs-info.patch b/patches.suse/msft-hv-1473-vmbus-add-per-channel-sysfs-info.patch
index bb68a6d3d6..963e561e50 100644
--- a/patches.suse/msft-hv-1473-vmbus-add-per-channel-sysfs-info.patch
+++ b/patches.suse/msft-hv-1473-vmbus-add-per-channel-sysfs-info.patch
@@ -115,7 +115,7 @@ diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
+
if (channel->sc_creation_callback != NULL)
channel->sc_creation_callback(newchannel);
- return;
+ newchannel->probe_done = true;
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
diff --git a/patches.suse/msft-hv-1507-Drivers-hv-vmbus-Fix-bugs-in-rescind-handling.patch b/patches.suse/msft-hv-1507-Drivers-hv-vmbus-Fix-bugs-in-rescind-handling.patch
new file mode 100644
index 0000000000..a16cd290d7
--- /dev/null
+++ b/patches.suse/msft-hv-1507-Drivers-hv-vmbus-Fix-bugs-in-rescind-handling.patch
@@ -0,0 +1,196 @@
+From: "K. Y. Srinivasan" <kys@microsoft.com>
+Date: Fri, 29 Sep 2017 21:09:36 -0700
+Patch-mainline: v4.14-rc5
+Subject: Drivers: hv: vmbus: Fix bugs in rescind handling
+Git-commit: 192b2d78722ffea188e5ec6ae5d55010dce05a4b
+References: bsc#1130567
+
+This patch addresses the following bugs in the current rescind handling code:
+
+1. Fixes a race condition where we may be invoking hv_process_channel_removal()
+on an already freed channel.
+
+2. Prevents indefinite wait when rescinding sub-channels by correctly setting
+the probe_complete state.
+
+I would like to thank Dexuan for patiently reviewing earlier versions of this
+patch and identifying many of the issues fixed here.
+
+Greg, please apply this to 4.14-final.
+
+Fixes: '54a66265d675 ("Drivers: hv: vmbus: Fix rescind handling")'
+
+Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
+Reviewed-by: Dexuan Cui <decui@microsoft.com>
+Cc: stable@vger.kernel.org # (4.13 and above)
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Acked-by: Olaf Hering <ohering@suse.de>
+---
+ drivers/hv/channel.c | 6 +++---
+ drivers/hv/channel_mgmt.c | 37 ++++++++++++++++++-------------------
+ drivers/hv/vmbus_drv.c | 3 +--
+ include/linux/hyperv.h | 2 +-
+ 4 files changed, 23 insertions(+), 25 deletions(-)
+
+diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c
+--- a/drivers/hv/channel.c
++++ b/drivers/hv/channel.c
+@@ -640,6 +640,7 @@ void vmbus_close(struct vmbus_channel *channel)
+ */
+ return;
+ }
++ mutex_lock(&vmbus_connection.channel_mutex);
+ /*
+ * Close all the sub-channels first and then close the
+ * primary channel.
+@@ -648,16 +649,15 @@ void vmbus_close(struct vmbus_channel *channel)
+ cur_channel = list_entry(cur, struct vmbus_channel, sc_list);
+ vmbus_close_internal(cur_channel);
+ if (cur_channel->rescind) {
+- mutex_lock(&vmbus_connection.channel_mutex);
+- hv_process_channel_removal(cur_channel,
++ hv_process_channel_removal(
+ cur_channel->offermsg.child_relid);
+- mutex_unlock(&vmbus_connection.channel_mutex);
+ }
+ }
+ /*
+ * Now close the primary.
+ */
+ vmbus_close_internal(channel);
++ mutex_unlock(&vmbus_connection.channel_mutex);
+ }
+ EXPORT_SYMBOL_GPL(vmbus_close);
+
+diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
+--- a/drivers/hv/channel_mgmt.c
++++ b/drivers/hv/channel_mgmt.c
+@@ -159,7 +159,7 @@ static void vmbus_rescind_cleanup(struct vmbus_channel *channel)
+
+
+ spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
+-
++ channel->rescind = true;
+ list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list,
+ msglistentry) {
+
+@@ -381,14 +381,21 @@ static void vmbus_release_relid(u32 relid)
+ vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released));
+ }
+
+-void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
++void hv_process_channel_removal(u32 relid)
+ {
+ unsigned long flags;
+- struct vmbus_channel *primary_channel;
++ struct vmbus_channel *primary_channel, *channel;
+
+- BUG_ON(!channel->rescind);
+ BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex));
+
++ /*
++ * Make sure channel is valid as we may have raced.
++ */
++ channel = relid2channel(relid);
++ if (!channel)
++ return;
++
++ BUG_ON(!channel->rescind);
+ if (channel->target_cpu != get_cpu()) {
+ put_cpu();
+ smp_call_function_single(channel->target_cpu,
+@@ -515,6 +522,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
+ if (!fnew) {
+ if (channel->sc_creation_callback != NULL)
+ channel->sc_creation_callback(newchannel);
++ newchannel->probe_done = true;
+ return;
+ }
+
+@@ -834,7 +842,6 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
+ {
+ struct vmbus_channel_rescind_offer *rescind;
+ struct vmbus_channel *channel;
+- unsigned long flags;
+ struct device *dev;
+
+ rescind = (struct vmbus_channel_rescind_offer *)hdr;
+@@ -873,16 +880,6 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
+ return;
+ }
+
+- spin_lock_irqsave(&channel->lock, flags);
+- channel->rescind = true;
+- spin_unlock_irqrestore(&channel->lock, flags);
+-
+- /*
+- * Now that we have posted the rescind state, perform
+- * rescind related cleanup.
+- */
+- vmbus_rescind_cleanup(channel);
+-
+ /*
+ * Now wait for offer handling to complete.
+ */
+@@ -901,6 +898,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
+ if (channel->device_obj) {
+ if (channel->chn_rescind_callback) {
+ channel->chn_rescind_callback(channel);
++ vmbus_rescind_cleanup(channel);
+ return;
+ }
+ /*
+@@ -909,6 +907,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
+ */
+ dev = get_device(&channel->device_obj->device);
+ if (dev) {
++ vmbus_rescind_cleanup(channel);
+ vmbus_device_unregister(channel->device_obj);
+ put_device(dev);
+ }
+@@ -921,16 +920,16 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
+ * 1. Close all sub-channels first
+ * 2. Then close the primary channel.
+ */
++ mutex_lock(&vmbus_connection.channel_mutex);
++ vmbus_rescind_cleanup(channel);
+ if (channel->state == CHANNEL_OPEN_STATE) {
+ /*
+ * The channel is currently not open;
+ * it is safe for us to cleanup the channel.
+ */
+- mutex_lock(&vmbus_connection.channel_mutex);
+- hv_process_channel_removal(channel,
+- channel->offermsg.child_relid);
+- mutex_unlock(&vmbus_connection.channel_mutex);
++ hv_process_channel_removal(rescind->child_relid);
+ }
++ mutex_unlock(&vmbus_connection.channel_mutex);
+ }
+ }
+
+diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
+--- a/drivers/hv/vmbus_drv.c
++++ b/drivers/hv/vmbus_drv.c
+@@ -768,8 +768,7 @@ static void vmbus_device_release(struct device *device)
+ struct vmbus_channel *channel = hv_dev->channel;
+
+ mutex_lock(&vmbus_connection.channel_mutex);
+- hv_process_channel_removal(channel,
+- channel->offermsg.child_relid);
++ hv_process_channel_removal(channel->offermsg.child_relid);
+ mutex_unlock(&vmbus_connection.channel_mutex);
+ kfree(hv_dev);
+
+diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
+--- a/include/linux/hyperv.h
++++ b/include/linux/hyperv.h
+@@ -1403,7 +1403,7 @@ extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, u8 *buf,
+ const int *srv_version, int srv_vercnt,
+ int *nego_fw_version, int *nego_srv_version);
+
+-void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid);
++void hv_process_channel_removal(u32 relid);
+
+ void vmbus_setevent(struct vmbus_channel *channel);
+ /*
diff --git a/patches.suse/msft-hv-1721-Drivers-hv-vmbus-Fix-the-offer_in_progress-in-vmbus_.patch b/patches.suse/msft-hv-1721-Drivers-hv-vmbus-Fix-the-offer_in_progress-in-vmbus_.patch
new file mode 100644
index 0000000000..c470809ee9
--- /dev/null
+++ b/patches.suse/msft-hv-1721-Drivers-hv-vmbus-Fix-the-offer_in_progress-in-vmbus_.patch
@@ -0,0 +1,39 @@
+From: Dexuan Cui <decui@microsoft.com>
+Date: Tue, 5 Jun 2018 13:37:52 -0700
+Patch-mainline: v4.19-rc1
+Subject: Drivers: hv: vmbus: Fix the offer_in_progress in vmbus_process_offer()
+Git-commit: 50229128727f7e11840ca1b2b501f880818d56b6
+References: bsc#1130567
+
+I didn't really hit a real bug, but just happened to spot the bug:
+we have decreased the counter at the beginning of vmbus_process_offer(),
+so we mustn't decrease it again.
+
+Fixes: 6f3d791f3006 ("Drivers: hv: vmbus: Fix rescind handling issues")
+Signed-off-by: Dexuan Cui <decui@microsoft.com>
+Cc: stable@vger.kernel.org
+Cc: Stephen Hemminger <sthemmin@microsoft.com>
+Cc: K. Y. Srinivasan <kys@microsoft.com>
+Cc: Stable <stable@vger.kernel.org> # 4.14 and above
+Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Acked-by: Olaf Hering <ohering@suse.de>
+---
+ drivers/hv/channel_mgmt.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
+--- a/drivers/hv/channel_mgmt.c
++++ b/drivers/hv/channel_mgmt.c
+@@ -527,10 +527,8 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
+ struct hv_device *dev
+ = newchannel->primary_channel->device_obj;
+
+- if (vmbus_add_channel_kobj(dev, newchannel)) {
+- atomic_dec(&vmbus_connection.offer_in_progress);
++ if (vmbus_add_channel_kobj(dev, newchannel))
+ goto err_free_chan;
+- }
+
+ if (channel->sc_creation_callback != NULL)
+ channel->sc_creation_callback(newchannel);
diff --git a/patches.suse/msft-hv-1742-Drivers-hv-vmbus-Reset-the-channel-callback-in-vmbus.patch b/patches.suse/msft-hv-1742-Drivers-hv-vmbus-Reset-the-channel-callback-in-vmbus.patch
new file mode 100644
index 0000000000..4cb2042ede
--- /dev/null
+++ b/patches.suse/msft-hv-1742-Drivers-hv-vmbus-Reset-the-channel-callback-in-vmbus.patch
@@ -0,0 +1,135 @@
+From: Dexuan Cui <decui@microsoft.com>
+Date: Thu, 2 Aug 2018 03:08:23 +0000
+Patch-mainline: v4.19-rc1
+Subject: Drivers: hv: vmbus: Reset the channel callback in vmbus_onoffer_rescind()
+Git-commit: d3b26dd7cb0e3433bfd3c1d4dcf74c6039bb49fb
+References: bsc#1130567
+
+Before setting channel->rescind in vmbus_rescind_cleanup(), we should make
+sure the channel callback won't run any more, otherwise a high-level
+driver like pci_hyperv, which may be infinitely waiting for the host VSP's
+response and notices the channel has been rescinded, can't safely give
+up: e.g., in hv_pci_protocol_negotiation() -> wait_for_response(), it's
+unsafe to exit from wait_for_response() and proceed with the on-stack
+variable "comp_pkt" popped. The issue was originally spotted by
+Michael Kelley <mikelley@microsoft.com>.
+
+In vmbus_close_internal(), the patch also minimizes the range protected by
+disabling/enabling channel->callback_event: we don't really need that for
+the whole function.
+
+Signed-off-by: Dexuan Cui <decui@microsoft.com>
+Reviewed-by: Michael Kelley <mikelley@microsoft.com>
+Cc: stable@vger.kernel.org
+Cc: K. Y. Srinivasan <kys@microsoft.com>
+Cc: Stephen Hemminger <sthemmin@microsoft.com>
+Cc: Michael Kelley <mikelley@microsoft.com>
+Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Acked-by: Olaf Hering <ohering@suse.de>
+---
+ drivers/hv/channel.c | 40 ++++++++++++++++++++++++----------------
+ drivers/hv/channel_mgmt.c | 6 ++++++
+ include/linux/hyperv.h | 2 ++
+ 3 files changed, 32 insertions(+), 16 deletions(-)
+
+diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c
+--- a/drivers/hv/channel.c
++++ b/drivers/hv/channel.c
+@@ -558,11 +558,8 @@ static void reset_channel_cb(void *arg)
+ channel->onchannel_callback = NULL;
+ }
+
+-static int vmbus_close_internal(struct vmbus_channel *channel)
++void vmbus_reset_channel_cb(struct vmbus_channel *channel)
+ {
+- struct vmbus_channel_close_channel *msg;
+- int ret;
+-
+ /*
+ * vmbus_on_event(), running in the per-channel tasklet, can race
+ * with vmbus_close_internal() in the case of SMP guest, e.g., when
+@@ -572,6 +569,29 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
+ */
+ tasklet_disable(&channel->callback_event);
+
++ channel->sc_creation_callback = NULL;
++
++ /* Stop the callback asap */
++ if (channel->target_cpu != get_cpu()) {
++ put_cpu();
++ smp_call_function_single(channel->target_cpu, reset_channel_cb,
++ channel, true);
++ } else {
++ reset_channel_cb(channel);
++ put_cpu();
++ }
++
++ /* Re-enable tasklet for use on re-open */
++ tasklet_enable(&channel->callback_event);
++}
++
++static int vmbus_close_internal(struct vmbus_channel *channel)
++{
++ struct vmbus_channel_close_channel *msg;
++ int ret;
++
++ vmbus_reset_channel_cb(channel);
++
+ /*
+ * In case a device driver's probe() fails (e.g.,
+ * util_probe() -> vmbus_open() returns -ENOMEM) and the device is
+@@ -585,16 +605,6 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
+ }
+
+ channel->state = CHANNEL_OPEN_STATE;
+- channel->sc_creation_callback = NULL;
+- /* Stop callback and cancel the timer asap */
+- if (channel->target_cpu != get_cpu()) {
+- put_cpu();
+- smp_call_function_single(channel->target_cpu, reset_channel_cb,
+- channel, true);
+- } else {
+- reset_channel_cb(channel);
+- put_cpu();
+- }
+
+ /* Send a closing message */
+
+@@ -639,8 +649,6 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
+ get_order(channel->ringbuffer_pagecount * PAGE_SIZE));
+
+ out:
+- /* re-enable tasklet for use on re-open */
+- tasklet_enable(&channel->callback_event);
+ return ret;
+ }
+
+diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
+--- a/drivers/hv/channel_mgmt.c
++++ b/drivers/hv/channel_mgmt.c
+@@ -892,6 +892,12 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
+ return;
+ }
+
++ /*
++ * Before setting channel->rescind in vmbus_rescind_cleanup(), we
++ * should make sure the channel callback is not running any more.
++ */
++ vmbus_reset_channel_cb(channel);
++
+ /*
+ * Now wait for offer handling to complete.
+ */
+diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
+--- a/include/linux/hyperv.h
++++ b/include/linux/hyperv.h
+@@ -1061,6 +1061,8 @@ extern int vmbus_establish_gpadl(struct vmbus_channel *channel,
+ extern int vmbus_teardown_gpadl(struct vmbus_channel *channel,
+ u32 gpadl_handle);
+
++void vmbus_reset_channel_cb(struct vmbus_channel *channel);
++
+ extern int vmbus_recvpacket(struct vmbus_channel *channel,
+ void *buffer,
+ u32 bufferlen,
diff --git a/patches.suse/msft-hv-1803-Drivers-hv-vmbus-Offload-the-handling-of-channels-to.patch b/patches.suse/msft-hv-1803-Drivers-hv-vmbus-Offload-the-handling-of-channels-to.patch
new file mode 100644
index 0000000000..de41d0abf9
--- /dev/null
+++ b/patches.suse/msft-hv-1803-Drivers-hv-vmbus-Offload-the-handling-of-channels-to.patch
@@ -0,0 +1,465 @@
+From: Dexuan Cui <decui@microsoft.com>
+Date: Mon, 3 Dec 2018 00:54:35 +0000
+Patch-mainline: v4.20-rc6
+Subject: Drivers: hv: vmbus: Offload the handling of channels to two workqueues
+Git-commit: 37c2578c0c40e286bc0d30bdc05290b2058cf66e
+References: bsc#1130567
+
+vmbus_process_offer() mustn't call channel->sc_creation_callback()
+directly for sub-channels, because sc_creation_callback() ->
+vmbus_open() may never get the host's response to the
+OPEN_CHANNEL message (the host may rescind a channel at any time,
+e.g. in the case of hot removing a NIC), and vmbus_onoffer_rescind()
+may not wake up the vmbus_open() as it's blocked due to a non-zero
+vmbus_connection.offer_in_progress, and finally we have a deadlock.
+
+The above is also true for primary channels, if the related device
+drivers use sync probing mode by default.
+
+And, usually the handling of primary channels and sub-channels can
+depend on each other, so we should offload them to different
+workqueues to avoid possible deadlock, e.g. in sync-probing mode,
+NIC1's netvsc_subchan_work() can race with NIC2's netvsc_probe() ->
+rtnl_lock(), and causes deadlock: the former gets the rtnl_lock
+and waits for all the sub-channels to appear, but the latter
+can't get the rtnl_lock and this blocks the handling of sub-channels.
+
+The patch can fix the multiple-NIC deadlock described above for
+v3.x kernels (e.g. RHEL 7.x) which don't support async-probing
+of devices, and v4.4, v4.9, v4.14 and v4.18 which support async-probing
+but don't enable async-probing for Hyper-V drivers (yet).
+
+The patch can also fix the hang issue in sub-channel's handling described
+above for all versions of kernels, including v4.19 and v4.20-rc4.
+
+So actually the patch should be applied to all the existing kernels,
+not only the kernels that have 8195b1396ec8.
+
+Slightly adjusted to preserver kABI.
+
+Fixes: 8195b1396ec8 ("hv_netvsc: fix deadlock on hotplug")
+Cc: stable@vger.kernel.org
+Cc: Stephen Hemminger <sthemmin@microsoft.com>
+Cc: K. Y. Srinivasan <kys@microsoft.com>
+Cc: Haiyang Zhang <haiyangz@microsoft.com>
+Signed-off-by: Dexuan Cui <decui@microsoft.com>
+Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Acked-by: Olaf Hering <ohering@suse.de>
+---
+ drivers/hv/channel_mgmt.c | 189 ++++++++++++++++++++++++++++++----------------
+ drivers/hv/connection.c | 24 +++++-
+ drivers/hv/hyperv_vmbus.h | 7 ++
+ include/linux/hyperv.h | 7 ++
+ 4 files changed, 161 insertions(+), 66 deletions(-)
+
+--- a/drivers/hv/channel_mgmt.c
++++ b/drivers/hv/channel_mgmt.c
+@@ -327,11 +327,23 @@ EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_r
+ static struct vmbus_channel *alloc_channel(void)
+ {
+ struct vmbus_channel *channel;
++ struct vmbus_channel_aufschnitt *brot;
++ unsigned long scheibchen;
+
+ channel = kzalloc(sizeof(*channel), GFP_ATOMIC);
+ if (!channel)
+ return NULL;
+
++ /* This may leak, but the previous allocation does apparently too */
++ brot = kzalloc(sizeof(*brot), GFP_ATOMIC);
++ if (!brot)
++ return NULL;
++
++ brot->belag = channel;
++ scheibchen = (unsigned long)brot;
++ channel->tilsiter = scheibchen >> 32UL;
++ channel->morbier = scheibchen & 0xffffffffUL;
++
+ spin_lock_init(&channel->lock);
+
+ INIT_LIST_HEAD(&channel->sc_list);
+@@ -442,61 +454,17 @@ void vmbus_free_channels(void)
+ }
+ }
+
+-/*
+- * vmbus_process_offer - Process the offer by creating a channel/device
+- * associated with this offer
+- */
+-static void vmbus_process_offer(struct vmbus_channel *newchannel)
++/* Note: the function can run concurrently for primary/sub channels. */
++static void vmbus_add_channel_work(struct work_struct *work)
+ {
+- struct vmbus_channel *channel;
+- bool fnew = true;
++ struct vmbus_channel_aufschnitt *brot =
++ container_of(work, struct vmbus_channel_aufschnitt, add_channel_work);
++ struct vmbus_channel *newchannel = brot->belag;
++ struct vmbus_channel *primary_channel = newchannel->primary_channel;
+ unsigned long flags;
+ u16 dev_type;
+ int ret;
+
+- /* Make sure this is a new offer */
+- mutex_lock(&vmbus_connection.channel_mutex);
+-
+- /*
+- * Now that we have acquired the channel_mutex,
+- * we can release the potentially racing rescind thread.
+- */
+- atomic_dec(&vmbus_connection.offer_in_progress);
+-
+- list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
+- if (!uuid_le_cmp(channel->offermsg.offer.if_type,
+- newchannel->offermsg.offer.if_type) &&
+- !uuid_le_cmp(channel->offermsg.offer.if_instance,
+- newchannel->offermsg.offer.if_instance)) {
+- fnew = false;
+- break;
+- }
+- }
+-
+- if (fnew)
+- list_add_tail(&newchannel->listentry,
+- &vmbus_connection.chn_list);
+-
+- mutex_unlock(&vmbus_connection.channel_mutex);
+-
+- if (!fnew) {
+- /*
+- * Check to see if this is a sub-channel.
+- */
+- if (newchannel->offermsg.offer.sub_channel_index != 0) {
+- /*
+- * Process the sub-channel.
+- */
+- newchannel->primary_channel = channel;
+- spin_lock_irqsave(&channel->lock, flags);
+- list_add_tail(&newchannel->sc_list, &channel->sc_list);
+- channel->num_sc++;
+- spin_unlock_irqrestore(&channel->lock, flags);
+- } else {
+- goto err_free_chan;
+- }
+- }
+-
+ dev_type = hv_get_dev_type(newchannel);
+
+ init_vp_index(newchannel, dev_type);
+@@ -514,27 +482,26 @@ static void vmbus_process_offer(struct v
+ /*
+ * This state is used to indicate a successful open
+ * so that when we do close the channel normally, we
+- * can cleanup properly
++ * can cleanup properly.
+ */
+ newchannel->state = CHANNEL_OPEN_STATE;
+
+- if (!fnew) {
+- struct hv_device *dev
+- = newchannel->primary_channel->device_obj;
++ if (primary_channel != NULL) {
++ /* newchannel is a sub-channel. */
++ struct hv_device *dev = primary_channel->device_obj;
+
+ if (vmbus_add_channel_kobj(dev, newchannel))
+- goto err_free_chan;
++ goto err_deq_chan;
++
++ if (primary_channel->sc_creation_callback != NULL)
++ primary_channel->sc_creation_callback(newchannel);
+
+- if (channel->sc_creation_callback != NULL)
+- channel->sc_creation_callback(newchannel);
+ newchannel->probe_done = true;
+ return;
+ }
+
+ /*
+- * Start the process of binding this offer to the driver
+- * We need to set the DeviceObject field before calling
+- * vmbus_child_dev_add()
++ * Start the process of binding the primary channel to the driver
+ */
+ newchannel->device_obj = vmbus_device_create(
+ &newchannel->offermsg.offer.if_type,
+@@ -563,13 +530,28 @@ static void vmbus_process_offer(struct v
+
+ err_deq_chan:
+ mutex_lock(&vmbus_connection.channel_mutex);
+- list_del(&newchannel->listentry);
++
++ /*
++ * We need to set the flag, otherwise
++ * vmbus_onoffer_rescind() can be blocked.
++ */
++ newchannel->probe_done = true;
++
++ if (primary_channel == NULL) {
++ list_del(&newchannel->listentry);
++ } else {
++ spin_lock_irqsave(&primary_channel->lock, flags);
++ list_del(&newchannel->sc_list);
++ spin_unlock_irqrestore(&primary_channel->lock, flags);
++ }
++
+ mutex_unlock(&vmbus_connection.channel_mutex);
+
+ if (newchannel->target_cpu != get_cpu()) {
+ put_cpu();
+ smp_call_function_single(newchannel->target_cpu,
+- percpu_channel_deq, newchannel, true);
++ percpu_channel_deq,
++ newchannel, true);
+ } else {
+ percpu_channel_deq(newchannel);
+ put_cpu();
+@@ -577,14 +559,113 @@ err_deq_chan:
+
+ vmbus_release_relid(newchannel->offermsg.child_relid);
+
+-err_free_chan:
+ free_channel(newchannel);
+ }
+
+ /*
++ * vmbus_process_offer - Process the offer by creating a channel/device
++ * associated with this offer
++ */
++static void vmbus_process_offer(struct vmbus_channel *newchannel)
++{
++ struct vmbus_channel *channel;
++ struct vmbus_channel_aufschnitt *brot;
++ unsigned long scheibchen;
++ struct workqueue_struct *wq;
++ unsigned long flags;
++ bool fnew = true;
++
++ scheibchen = newchannel->tilsiter;
++ scheibchen <<= 32UL;
++ scheibchen |= newchannel->morbier;
++ brot = (struct vmbus_channel_aufschnitt *)scheibchen;
++ channel = brot->belag;
++
++ mutex_lock(&vmbus_connection.channel_mutex);
++
++ /*
++ * Now that we have acquired the channel_mutex,
++ * we can release the potentially racing rescind thread.
++ */
++ atomic_dec(&vmbus_connection.offer_in_progress);
++
++ list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
++ if (!uuid_le_cmp(channel->offermsg.offer.if_type,
++ newchannel->offermsg.offer.if_type) &&
++ !uuid_le_cmp(channel->offermsg.offer.if_instance,
++ newchannel->offermsg.offer.if_instance)) {
++ fnew = false;
++ break;
++ }
++ }
++
++ if (fnew)
++ list_add_tail(&newchannel->listentry,
++ &vmbus_connection.chn_list);
++ else {
++ /*
++ * Check to see if this is a valid sub-channel.
++ */
++ if (newchannel->offermsg.offer.sub_channel_index == 0) {
++ mutex_unlock(&vmbus_connection.channel_mutex);
++ /*
++ * Don't call free_channel(), because newchannel->kobj
++ * is not initialized yet.
++ */
++ kfree(brot);
++ kfree(newchannel);
++ WARN_ON_ONCE(1);
++ return;
++ }
++ /*
++ * Process the sub-channel.
++ */
++ newchannel->primary_channel = channel;
++ spin_lock_irqsave(&channel->lock, flags);
++ list_add_tail(&newchannel->sc_list, &channel->sc_list);
++ spin_unlock_irqrestore(&channel->lock, flags);
++ }
++
++ mutex_unlock(&vmbus_connection.channel_mutex);
++
++ /*
++ * vmbus_process_offer() mustn't call channel->sc_creation_callback()
++ * directly for sub-channels, because sc_creation_callback() ->
++ * vmbus_open() may never get the host's response to the
++ * OPEN_CHANNEL message (the host may rescind a channel at any time,
++ * e.g. in the case of hot removing a NIC), and vmbus_onoffer_rescind()
++ * may not wake up the vmbus_open() as it's blocked due to a non-zero
++ * vmbus_connection.offer_in_progress, and finally we have a deadlock.
++ *
++ * The above is also true for primary channels, if the related device
++ * drivers use sync probing mode by default.
++ *
++ * And, usually the handling of primary channels and sub-channels can
++ * depend on each other, so we should offload them to different
++ * workqueues to avoid possible deadlock, e.g. in sync-probing mode,
++ * NIC1's netvsc_subchan_work() can race with NIC2's netvsc_probe() ->
++ * rtnl_lock(), and causes deadlock: the former gets the rtnl_lock
++ * and waits for all the sub-channels to appear, but the latter
++ * can't get the rtnl_lock and this blocks the handling of
++ * sub-channels.
++ */
++ INIT_WORK(&brot->add_channel_work, vmbus_add_channel_work);
++ wq = fnew ? vmbus_connection_handle_primary_chan_wq :
++ vmbus_connection_handle_sub_chan_wq;
++ queue_work(wq, &brot->add_channel_work);
++}
++
++/*
+ * We use this state to statically distribute the channel interrupt load.
+ */
+ static int next_numa_node_id;
++/*
++ * init_vp_index() accesses global variables like next_numa_node_id, and
++ * it can run concurrently for primary channels and sub-channels: see
++ * vmbus_process_offer(), so we need the lock to protect the global
++ * variables.
++ */
++static DEFINE_SPINLOCK(bind_channel_to_cpu_lock);
+
+ /*
+ * Starting with Win8, we can statically distribute the incoming
+@@ -620,6 +701,8 @@ static void init_vp_index(struct vmbus_c
+ return;
+ }
+
++ spin_lock(&bind_channel_to_cpu_lock);
++
+ /*
+ * Based on the channel affinity policy, we will assign the NUMA
+ * nodes.
+@@ -701,6 +784,8 @@ static void init_vp_index(struct vmbus_c
+
+ channel->target_cpu = cur_cpu;
+ channel->target_vp = hv_context.vp_index[cur_cpu];
++
++ spin_unlock(&bind_channel_to_cpu_lock);
+ }
+
+ static void vmbus_wait_for_unload(void)
+--- a/drivers/hv/connection.c
++++ b/drivers/hv/connection.c
+@@ -37,6 +37,11 @@
+ #include "hyperv_vmbus.h"
+
+
++/* handle_primary_chan_wq */
++struct workqueue_struct *vmbus_connection_handle_primary_chan_wq;
++/* handle_sub_chan_wq */
++struct workqueue_struct *vmbus_connection_handle_sub_chan_wq;
++
+ struct vmbus_connection vmbus_connection = {
+ .conn_state = DISCONNECTED,
+ .next_gpadl_handle = ATOMIC_INIT(0xE1E10),
+@@ -157,6 +162,20 @@ int vmbus_connect(void)
+ goto cleanup;
+ }
+
++ vmbus_connection_handle_primary_chan_wq =
++ create_workqueue("hv_pri_chan");
++ if (!vmbus_connection_handle_primary_chan_wq) {
++ ret = -ENOMEM;
++ goto cleanup;
++ }
++
++ vmbus_connection_handle_sub_chan_wq =
++ create_workqueue("hv_sub_chan");
++ if (!vmbus_connection_handle_sub_chan_wq) {
++ ret = -ENOMEM;
++ goto cleanup;
++ }
++
+ INIT_LIST_HEAD(&vmbus_connection.chn_msg_list);
+ spin_lock_init(&vmbus_connection.channelmsg_lock);
+
+@@ -247,10 +266,14 @@ void vmbus_disconnect(void)
+ */
+ vmbus_initiate_unload(false);
+
+- if (vmbus_connection.work_queue) {
+- drain_workqueue(vmbus_connection.work_queue);
++ if (vmbus_connection_handle_sub_chan_wq)
++ destroy_workqueue(vmbus_connection_handle_sub_chan_wq);
++
++ if (vmbus_connection_handle_primary_chan_wq)
++ destroy_workqueue(vmbus_connection_handle_primary_chan_wq);
++
++ if (vmbus_connection.work_queue)
+ destroy_workqueue(vmbus_connection.work_queue);
+- }
+
+ if (vmbus_connection.int_page) {
+ free_pages((unsigned long)vmbus_connection.int_page, 0);
+--- a/drivers/hv/hyperv_vmbus.h
++++ b/drivers/hv/hyperv_vmbus.h
+@@ -340,9 +340,16 @@ struct vmbus_connection {
+ struct list_head chn_list;
+ struct mutex channel_mutex;
+
++ /*
++ * An offer message is handled first on the work_queue, and then
++ * is further handled on handle_primary_chan_wq or
++ * handle_sub_chan_wq.
++ */
+ struct workqueue_struct *work_queue;
+ };
+
++extern struct workqueue_struct *vmbus_connection_handle_primary_chan_wq;
++extern struct workqueue_struct *vmbus_connection_handle_sub_chan_wq;
+
+ struct vmbus_msginfo {
+ /* Bookkeeping stuff */
+--- a/include/linux/hyperv.h
++++ b/include/linux/hyperv.h
+@@ -714,6 +714,11 @@ struct vmbus_channel {
+ /* Allocated memory for ring buffer */
+ void *ringbuffer_pages;
+ u32 ringbuffer_pagecount;
++
++#ifndef __GENKSYMS__
++ uint32_t tilsiter;
++#endif
++
+ struct hv_ring_buffer_info outbound; /* send to parent */
+ struct hv_ring_buffer_info inbound; /* receive from parent */
+
+@@ -760,6 +765,10 @@ struct vmbus_channel {
+ */
+ struct cpumask alloced_cpus_in_node;
+ int numa_node;
++#ifndef __GENKSYMS__
++ uint32_t morbier;
++#endif
++
+ /*
+ * Support for sub-channels. For high performance devices,
+ * it will be useful to have multiple sub-channels to support
+@@ -872,7 +881,17 @@ struct vmbus_channel {
+ enum hv_numa_policy affinity_policy;
+
+ bool probe_done;
++};
+
++struct vmbus_channel_aufschnitt {
++ struct vmbus_channel *belag;
++ /*
++ * We must offload the handling of the primary/sub channels
++ * from the single-threaded vmbus_connection.work_queue to
++ * two different workqueue, otherwise we can block
++ * vmbus_connection.work_queue and hang: see vmbus_process_offer().
++ */
++ struct work_struct add_channel_work;
+ };
+
+ static inline bool is_hvsock_channel(const struct vmbus_channel *c)
diff --git a/series.conf b/series.conf
index 858cdcae85..6fbd28d115 100644
--- a/series.conf
+++ b/series.conf
@@ -22978,6 +22978,7 @@
patches.suse/powerpc-livepatch-fix-livepatch-stack-access.patch
patches.drivers/fix-mpage_writepage-for-pages-with-buffers.patch
patches.drivers/drm-i915-Read-timings-from-the-correct-transcoder-in
+ patches.suse/msft-hv-1507-Drivers-hv-vmbus-Fix-bugs-in-rescind-handling.patch
patches.fixes/scsi-fixup-kernel-warning-during-rmmod.patch
patches.drivers/scsi-libfc-fix-a-deadlock-in-fc_rport_work.patch
patches.drivers/scsi-qla2xxx-Fix-uninitialized-work-element.patch
@@ -24610,6 +24611,8 @@
patches.drivers/IB-core-type-promotion-bug-in-rdma_rw_init_one_mr.patch
patches.drivers/RDMA-bnxt_re-Fix-a-couple-off-by-one-bugs.patch
patches.fixes/RDMA-rw-Fix-rdma_rw_ctx_signature_init-kernel-doc-he.patch
+ patches.suse/msft-hv-1721-Drivers-hv-vmbus-Fix-the-offer_in_progress-in-vmbus_.patch
+ patches.suse/msft-hv-1742-Drivers-hv-vmbus-Reset-the-channel-callback-in-vmbus.patch
patches.fixes/ceph-fix-incorrect-use-of-strncpy.patch
patches.fixes/ceph-return-errors-from-posix_acl_equiv_mode-correctly.patch
patches.fixes/0001-libceph-store-ceph_auth_handshake-pointer-in-ceph_connection.patch
@@ -24839,6 +24842,7 @@
patches.fixes/nvme-validate-controller-state-before-rescheduling-k.patch
patches.drivers/revert-pci-aspm-do-not-initialize-link-state-when-aspm_disabled-is-set
patches.fixes/libnvdimm-pfn-pad-pfn-namespaces-relative-to-other-regions.patch
+ patches.suse/msft-hv-1803-Drivers-hv-vmbus-Offload-the-handling-of-channels-to.patch
patches.drivers/net-ibmvnic-Fix-RTNL-deadlock-during-device-reset.patch
patches.fixes/ipv4-ipv6-netfilter-Adjust-the-frag-mem-limit-when-t.patch
patches.drivers/net-mlx4_core-Correctly-set-PFC-param-if-global-paus.patch
@@ -24909,6 +24913,7 @@
patches.drivers/net-ena-update-driver-version-from-2.0.2-to-2.0.3.patch
patches.fixes/0003-drm-i915-Block-fbdev-HPD-processing-during-suspend.patch
patches.fixes/0004-drm-i915-Prevent-a-race-during-I915_GEM_MMAP-ioctl-w.patch
+ patches.suse/floppy-check_events-callback-should-not-return-a-neg.patch
patches.fixes/md-raid1-don-t-clear-bitmap-bits-on-interrupted-reco.patch
patches.suse/0001-net-dsa-bcm_sf2-Do-not-assume-DSA-master-supports-Wo.patch
patches.suse/mm-memory_hotplug-fix-off-by-one-in-is_pageblock_rem.patch