diff options
author | Dexuan Cui <decui@microsoft.com> | 2015-03-27 09:10:08 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-04-03 16:18:01 +0200 |
commit | 652594c7dfd9bf6392e3a727bc69d89a2562d953 (patch) | |
tree | 6f40c87328a8d8e2880d8a4a6feb3f3fdb1c6caf /drivers/hv/vmbus_drv.c | |
parent | 01081f5ab9916603555f236b11f76bb00e4e01e9 (diff) |
hv: run non-blocking message handlers in the dispatch tasklet
A work item in vmbus_connection.work_queue can sleep, waiting for a new
host message (usually it is some kind of "completion" message). Currently
the new message will be handled in the same workqueue, but since work items
in the workqueue is serialized, we actually have no chance to handle
the new message if the current work item is sleeping -- as as result, the
current work item will hang forever.
K. Y. has posted the below fix to resolve the issue:
Drivers: hv: vmbus: Perform device register in the per-channel work element
Actually we can simplify the fix by directly running non-blocking message
handlers in the dispatch tasklet (inspired by K. Y.).
This patch is the fundamental change. The following 2 patches will simplify
the message offering and rescind-offering handling a lot.
Signed-off-by: Dexuan Cui <decui@microsoft.com>
Cc: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/hv/vmbus_drv.c')
-rw-r--r-- | drivers/hv/vmbus_drv.c | 21 |
1 files changed, 18 insertions, 3 deletions
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 8313e25378cb..c85235e9f245 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -657,21 +657,36 @@ static void vmbus_on_msg_dpc(unsigned long data) void *page_addr = hv_context.synic_message_page[cpu]; struct hv_message *msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; + struct vmbus_channel_message_header *hdr; + struct vmbus_channel_message_table_entry *entry; struct onmessage_work_context *ctx; while (1) { - if (msg->header.message_type == HVMSG_NONE) { + if (msg->header.message_type == HVMSG_NONE) /* no msg */ break; - } else { + + hdr = (struct vmbus_channel_message_header *)msg->u.payload; + + if (hdr->msgtype >= CHANNELMSG_COUNT) { + WARN_ONCE(1, "unknown msgtype=%d\n", hdr->msgtype); + goto msg_handled; + } + + entry = &channel_message_table[hdr->msgtype]; + if (entry->handler_type == VMHT_BLOCKING) { ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC); if (ctx == NULL) continue; + INIT_WORK(&ctx->work, vmbus_onmessage_work); memcpy(&ctx->msg, msg, sizeof(*msg)); + queue_work(vmbus_connection.work_queue, &ctx->work); - } + } else + entry->message_handler(hdr); +msg_handled: msg->header.message_type = HVMSG_NONE; /* |