[PATCH] vmbus: fix missed ring events on boot

Stephen Hemminger stephen at networkplumber.org
Sat Mar 18 00:19:51 UTC 2017


During initialization, the channel initialization code schedules the
tasklet to scan the VMBUS receive event page (i.e. simulates an
interrupt). The problem was that it invokes the tasklet on a different
CPU from where it normally runs and therefore if an event is present,
it will clear the bit but not find the associated channel.

This can lead to missed events, typically stuck tasks, during bootup
when sub channels are being initialized. Typically seen as stuck
boot with 8 or more CPU's.

This patch is not necessary for upstream (4.11 and later) since
commit 631e63a9f346 ("vmbus: change to per channel tasklet").
This changed vmbus code to get rid of common tasklet which
caused the problem.

Cc: stable at vger.kernel.org
Signed-off-by: Stephen Hemminger <sthemmin at microsoft.com>
---
 drivers/hv/channel.c      | 10 +++++++++-
 drivers/hv/channel_mgmt.c | 26 +++++++++++++++-----------
 2 files changed, 24 insertions(+), 12 deletions(-)

diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c
index be34547cdb68..a8790498ba2b 100644
--- a/drivers/hv/channel.c
+++ b/drivers/hv/channel.c
@@ -609,7 +609,15 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
 		get_order(channel->ringbuffer_pagecount * PAGE_SIZE));
 
 out:
-	hv_event_tasklet_enable(channel);
+	if (channel->target_cpu != get_cpu()) {
+		smp_call_function_single(channel->target_cpu,
+					 (smp_call_func_t)hv_event_tasklet_enable,
+					 channel, true);
+		put_cpu();
+	} else {
+		put_cpu();
+		hv_event_tasklet_enable(channel);
+	}
 
 	return ret;
 }
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index 0af7e39006c8..021f6da1968d 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -321,12 +321,24 @@ static void free_channel(struct vmbus_channel *channel)
 	kfree(channel);
 }
 
-static void percpu_channel_enq(void *arg)
+static void percpu_channel_enable(void *arg)
 {
 	struct vmbus_channel *channel = arg;
 	int cpu = smp_processor_id();
+	struct tasklet_struct *tasklet
+		= hv_context.event_dpc[cpu];
+
+	/*
+	 * This state is used to indicate a successful open
+	 * so that when we do close the channel normally, we
+	 * can cleanup properly
+	 */
+	channel->state = CHANNEL_OPEN_STATE;
 
 	list_add_tail(&channel->percpu_list, &hv_context.percpu_list[cpu]);
+
+	tasklet_enable(tasklet);
+	tasklet_schedule(tasklet);
 }
 
 static void percpu_channel_deq(void *arg)
@@ -480,20 +492,12 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
 	if (newchannel->target_cpu != get_cpu()) {
 		put_cpu();
 		smp_call_function_single(newchannel->target_cpu,
-					 percpu_channel_enq,
+					 percpu_channel_enable,
 					 newchannel, true);
 	} else {
-		percpu_channel_enq(newchannel);
+		percpu_channel_enable(newchannel);
 		put_cpu();
 	}
-	hv_event_tasklet_enable(newchannel);
-
-	/*
-	 * This state is used to indicate a successful open
-	 * so that when we do close the channel normally, we
-	 * can cleanup properly
-	 */
-	newchannel->state = CHANNEL_OPEN_STATE;
 
 	if (!fnew) {
 		if (channel->sc_creation_callback != NULL)
-- 
2.11.0



More information about the devel mailing list