hv: run non-blocking message handlers in the dispatch tasklet
authorDexuan Cui <decui@microsoft.com>
Fri, 27 Mar 2015 16:10:08 +0000 (09:10 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 3 Apr 2015 14:18:01 +0000 (16:18 +0200)
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>
drivers/hv/channel_mgmt.c
drivers/hv/hyperv_vmbus.h
drivers/hv/vmbus_drv.c

index bb39705a89d961d1a9decbe0bc01889d26ccc9e7..287f07b1bef65828438429cbeaf8a3eafa07161d 100644 (file)
 
 #include "hyperv_vmbus.h"
 
-struct vmbus_channel_message_table_entry {
-       enum vmbus_channel_message_type message_type;
-       void (*message_handler)(struct vmbus_channel_message_header *msg);
-};
-
 struct vmbus_rescind_work {
        struct work_struct work;
        struct vmbus_channel *channel;
@@ -827,25 +822,25 @@ static void vmbus_onversion_response(
 }
 
 /* Channel message dispatch table */
-static struct vmbus_channel_message_table_entry
+struct vmbus_channel_message_table_entry
        channel_message_table[CHANNELMSG_COUNT] = {
-       {CHANNELMSG_INVALID,                    NULL},
-       {CHANNELMSG_OFFERCHANNEL,               vmbus_onoffer},
-       {CHANNELMSG_RESCIND_CHANNELOFFER,       vmbus_onoffer_rescind},
-       {CHANNELMSG_REQUESTOFFERS,              NULL},
-       {CHANNELMSG_ALLOFFERS_DELIVERED,        vmbus_onoffers_delivered},
-       {CHANNELMSG_OPENCHANNEL,                NULL},
-       {CHANNELMSG_OPENCHANNEL_RESULT, vmbus_onopen_result},
-       {CHANNELMSG_CLOSECHANNEL,               NULL},
-       {CHANNELMSG_GPADL_HEADER,               NULL},
-       {CHANNELMSG_GPADL_BODY,         NULL},
-       {CHANNELMSG_GPADL_CREATED,              vmbus_ongpadl_created},
-       {CHANNELMSG_GPADL_TEARDOWN,             NULL},
-       {CHANNELMSG_GPADL_TORNDOWN,             vmbus_ongpadl_torndown},
-       {CHANNELMSG_RELID_RELEASED,             NULL},
-       {CHANNELMSG_INITIATE_CONTACT,           NULL},
-       {CHANNELMSG_VERSION_RESPONSE,           vmbus_onversion_response},
-       {CHANNELMSG_UNLOAD,                     NULL},
+       {CHANNELMSG_INVALID,                    0, NULL},
+       {CHANNELMSG_OFFERCHANNEL,               0, vmbus_onoffer},
+       {CHANNELMSG_RESCIND_CHANNELOFFER,       0, vmbus_onoffer_rescind},
+       {CHANNELMSG_REQUESTOFFERS,              0, NULL},
+       {CHANNELMSG_ALLOFFERS_DELIVERED,        1, vmbus_onoffers_delivered},
+       {CHANNELMSG_OPENCHANNEL,                0, NULL},
+       {CHANNELMSG_OPENCHANNEL_RESULT,         1, vmbus_onopen_result},
+       {CHANNELMSG_CLOSECHANNEL,               0, NULL},
+       {CHANNELMSG_GPADL_HEADER,               0, NULL},
+       {CHANNELMSG_GPADL_BODY,                 0, NULL},
+       {CHANNELMSG_GPADL_CREATED,              1, vmbus_ongpadl_created},
+       {CHANNELMSG_GPADL_TEARDOWN,             0, NULL},
+       {CHANNELMSG_GPADL_TORNDOWN,             1, vmbus_ongpadl_torndown},
+       {CHANNELMSG_RELID_RELEASED,             0, NULL},
+       {CHANNELMSG_INITIATE_CONTACT,           0, NULL},
+       {CHANNELMSG_VERSION_RESPONSE,           1, vmbus_onversion_response},
+       {CHANNELMSG_UNLOAD,                     0, NULL},
 };
 
 /*
index c8e27e0fdc99744d6552a220a5b32056219017cb..f40a5a935ab69faa2536628e45a68de40e20cdc3 100644 (file)
@@ -685,6 +685,23 @@ struct vmbus_msginfo {
 
 extern struct vmbus_connection vmbus_connection;
 
+enum vmbus_message_handler_type {
+       /* The related handler can sleep. */
+       VMHT_BLOCKING = 0,
+
+       /* The related handler must NOT sleep. */
+       VMHT_NON_BLOCKING = 1,
+};
+
+struct vmbus_channel_message_table_entry {
+       enum vmbus_channel_message_type message_type;
+       enum vmbus_message_handler_type handler_type;
+       void (*message_handler)(struct vmbus_channel_message_header *msg);
+};
+
+extern struct vmbus_channel_message_table_entry
+       channel_message_table[CHANNELMSG_COUNT];
+
 /* General vmbus interface */
 
 struct hv_device *vmbus_device_create(const uuid_le *type,
index 8313e25378cb2804262b00fa3d5bbdccc1213a3e..c85235e9f245b6d210384673168bef12c6f9f0de 100644 (file)
@@ -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;
 
                /*