Author: delphij
Date: Tue Dec 29 08:19:43 2015
New Revision: 292861
URL: https://svnweb.freebsd.org/changeset/base/292861

Log:
  hyperv: vmbus: run non-blocking message handlers in vmbus_msg_swintr()
  
  We'll remove the per-channel control_work_queue because it can't properly
  do serialization of message handling, e.g., when there are 2 NIC devices,
  vmbus_channel_on_offer() -> hv_queue_work_item() has a race condition:
  for an SMP VM, vmbus_channel_process_offer() can run concurrently on
  different CPUs and if the second NIC's
  vmbus_channel_process_offer() -> hv_vmbus_child_device_register() runs
  first, the second NIC's name will be hn0 and the first NIC's name will
  be hn1!
  
  We can fix the race condition by removing the per-channel control_work_queue
  and run all the message handlers in the global
  hv_vmbus_g_connection.work_queue -- we'll do this in the next patch.
  
  With the coming next patch, we have to run the non-blocking handlers
  directly in the kernel thread vmbus_msg_swintr(), because the special
  handling of sub-channel: when a sub-channel (e.g., of the storvsc driver)
  is received and being handled in vmbus_channel_on_offer() running on the
  global hv_vmbus_g_connection.work_queue, vmbus_channel_process_offer()
  invokes channel->sc_creation_callback, i.e., storvsc_handle_sc_creation,
  and the callback will invoke hv_vmbus_channel_open() -> hv_vmbus_post_message
  and expect a further reply from the host, but the handling of the further
  messag can't be done because the current message's handling hasn't finished
  yet; as result, hv_vmbus_channel_open() -> sema_timedwait() will time out
  and th device can't work.
  
  Also renamed the handler type from hv_pfn_channel_msg_handler to
  vmbus_msg_handler: the 'pfn' and 'channel' in the old name make no sense.
  
  Submitted by: Dexuan Cui <decui microsoft com>
  Reviewed by:  royger
  MFC after:    2 weeks
  Differential Revision:        https://reviews.freebsd.org/D4596

Modified:
  head/sys/dev/hyperv/vmbus/hv_channel_mgmt.c
  head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c
  head/sys/dev/hyperv/vmbus/hv_vmbus_priv.h

Modified: head/sys/dev/hyperv/vmbus/hv_channel_mgmt.c
==============================================================================
--- head/sys/dev/hyperv/vmbus/hv_channel_mgmt.c Tue Dec 29 08:19:06 2015        
(r292860)
+++ head/sys/dev/hyperv/vmbus/hv_channel_mgmt.c Tue Dec 29 08:19:43 2015        
(r292861)
@@ -31,13 +31,6 @@
 
 #include "hv_vmbus_priv.h"
 
-typedef void (*hv_pfn_channel_msg_handler)(hv_vmbus_channel_msg_header* msg);
-
-typedef struct hv_vmbus_channel_msg_table_entry {
-       hv_vmbus_channel_msg_type    messageType;
-       hv_pfn_channel_msg_handler   messageHandler;
-} hv_vmbus_channel_msg_table_entry;
-
 /*
  * Internal functions
  */
@@ -55,29 +48,40 @@ static void vmbus_channel_on_version_res
  */
 hv_vmbus_channel_msg_table_entry
     g_channel_message_table[HV_CHANNEL_MESSAGE_COUNT] = {
-       { HV_CHANNEL_MESSAGE_INVALID, NULL },
-       { HV_CHANNEL_MESSAGE_OFFER_CHANNEL, vmbus_channel_on_offer },
+       { HV_CHANNEL_MESSAGE_INVALID,
+               0, NULL },
+       { HV_CHANNEL_MESSAGE_OFFER_CHANNEL,
+               0, vmbus_channel_on_offer },
        { HV_CHANNEL_MESSAGE_RESCIND_CHANNEL_OFFER,
-               vmbus_channel_on_offer_rescind },
-       { HV_CHANNEL_MESSAGE_REQUEST_OFFERS, NULL },
+               0, vmbus_channel_on_offer_rescind },
+       { HV_CHANNEL_MESSAGE_REQUEST_OFFERS,
+               0, NULL },
        { HV_CHANNEL_MESSAGE_ALL_OFFERS_DELIVERED,
-               vmbus_channel_on_offers_delivered },
-       { HV_CHANNEL_MESSAGE_OPEN_CHANNEL, NULL },
+               1, vmbus_channel_on_offers_delivered },
+       { HV_CHANNEL_MESSAGE_OPEN_CHANNEL,
+               0, NULL },
        { HV_CHANNEL_MESSAGE_OPEN_CHANNEL_RESULT,
-               vmbus_channel_on_open_result },
-       { HV_CHANNEL_MESSAGE_CLOSE_CHANNEL, NULL },
-       { HV_CHANNEL_MESSAGEL_GPADL_HEADER, NULL },
-       { HV_CHANNEL_MESSAGE_GPADL_BODY, NULL },
+               1, vmbus_channel_on_open_result },
+       { HV_CHANNEL_MESSAGE_CLOSE_CHANNEL,
+               0, NULL },
+       { HV_CHANNEL_MESSAGEL_GPADL_HEADER,
+               0, NULL },
+       { HV_CHANNEL_MESSAGE_GPADL_BODY,
+               0, NULL },
        { HV_CHANNEL_MESSAGE_GPADL_CREATED,
-               vmbus_channel_on_gpadl_created },
-       { HV_CHANNEL_MESSAGE_GPADL_TEARDOWN, NULL },
+               1, vmbus_channel_on_gpadl_created },
+       { HV_CHANNEL_MESSAGE_GPADL_TEARDOWN,
+               0, NULL },
        { HV_CHANNEL_MESSAGE_GPADL_TORNDOWN,
-               vmbus_channel_on_gpadl_torndown },
-       { HV_CHANNEL_MESSAGE_REL_ID_RELEASED, NULL },
-       { HV_CHANNEL_MESSAGE_INITIATED_CONTACT, NULL },
+               1, vmbus_channel_on_gpadl_torndown },
+       { HV_CHANNEL_MESSAGE_REL_ID_RELEASED,
+               0, NULL },
+       { HV_CHANNEL_MESSAGE_INITIATED_CONTACT,
+               0, NULL },
        { HV_CHANNEL_MESSAGE_VERSION_RESPONSE,
-               vmbus_channel_on_version_response },
-       { HV_CHANNEL_MESSAGE_UNLOAD, NULL }
+               1, vmbus_channel_on_version_response },
+       { HV_CHANNEL_MESSAGE_UNLOAD,
+               0, NULL }
 };
 
 

Modified: head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c
==============================================================================
--- head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c    Tue Dec 29 08:19:06 
2015        (r292860)
+++ head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c    Tue Dec 29 08:19:43 
2015        (r292861)
@@ -76,8 +76,12 @@ vmbus_msg_swintr(void *arg)
 {
        int                     cpu;
        void*                   page_addr;
+       hv_vmbus_channel_msg_header      *hdr;
+       hv_vmbus_channel_msg_table_entry *entry;
+       hv_vmbus_channel_msg_type msg_type;
        hv_vmbus_message*       msg;
        hv_vmbus_message*       copied;
+       static bool warned      = false;
 
        cpu = (int)(long)arg;
        KASSERT(cpu <= mp_maxid, ("VMBUS: vmbus_msg_swintr: "
@@ -87,9 +91,24 @@ vmbus_msg_swintr(void *arg)
        msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT;
 
        for (;;) {
-               if (msg->header.message_type == HV_MESSAGE_TYPE_NONE) {
+               if (msg->header.message_type == HV_MESSAGE_TYPE_NONE)
                        break; /* no message */
-               } else {
+
+               hdr = (hv_vmbus_channel_msg_header *)msg->u.payload;
+               msg_type = hdr->message_type;
+
+               if (msg_type >= HV_CHANNEL_MESSAGE_COUNT && !warned) {
+                       warned = true;
+                       printf("VMBUS: unknown message type = %d\n", msg_type);
+                       goto handled;
+               }
+
+               entry = &g_channel_message_table[msg_type];
+
+               if (entry->handler_no_sleep)
+                       entry->messageHandler(hdr);
+               else {
+
                        copied = malloc(sizeof(hv_vmbus_message),
                                        M_DEVBUF, M_NOWAIT);
                        KASSERT(copied != NULL,
@@ -97,11 +116,13 @@ vmbus_msg_swintr(void *arg)
                                        " hv_vmbus_message!"));
                        if (copied == NULL)
                                continue;
+
                        memcpy(copied, msg, sizeof(hv_vmbus_message));
                        hv_queue_work_item(hv_vmbus_g_connection.work_queue,
-                       hv_vmbus_on_channel_message, copied);
-           }
-
+                                          hv_vmbus_on_channel_message,
+                                          copied);
+               }
+handled:
            msg->header.message_type = HV_MESSAGE_TYPE_NONE;
 
            /*

Modified: head/sys/dev/hyperv/vmbus/hv_vmbus_priv.h
==============================================================================
--- head/sys/dev/hyperv/vmbus/hv_vmbus_priv.h   Tue Dec 29 08:19:06 2015        
(r292860)
+++ head/sys/dev/hyperv/vmbus/hv_vmbus_priv.h   Tue Dec 29 08:19:43 2015        
(r292861)
@@ -586,6 +586,16 @@ typedef enum {
 extern hv_vmbus_context                hv_vmbus_g_context;
 extern hv_vmbus_connection     hv_vmbus_g_connection;
 
+typedef void (*vmbus_msg_handler)(hv_vmbus_channel_msg_header *msg);
+
+typedef struct hv_vmbus_channel_msg_table_entry {
+       hv_vmbus_channel_msg_type    messageType;
+
+       bool   handler_no_sleep; /* true: the handler doesn't sleep */
+       vmbus_msg_handler   messageHandler;
+} hv_vmbus_channel_msg_table_entry;
+
+extern hv_vmbus_channel_msg_table_entry        g_channel_message_table[];
 
 /*
  * Private, VM Bus functions
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to