[PATCH] vmbus: fix missed ring events on boot

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxxxxx
Signed-off-by: Stephen Hemminger <sthemmin@xxxxxxxxxxxxx>
---
 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




[Index of Archives]     [Linux Kernel]     [Kernel Development Newbies]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Hiking]     [Linux Kernel]     [Linux SCSI]