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