Skip to content

Commit

Permalink
hyperv: vmbus: run non-blocking message handlers in vmbus_msg_swintr()
Browse files Browse the repository at this point in the history
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
  • Loading branch information
delphij authored and dcui committed Feb 4, 2016
1 parent d09fa99 commit b706b38
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 29 deletions.
52 changes: 28 additions & 24 deletions sys/dev/hyperv/vmbus/hv_channel_mgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,6 @@ __FBSDID("$FreeBSD$");

#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
*/
Expand All @@ -61,29 +54,40 @@ struct hv_vmbus_channel*
*/
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 }
};


Expand Down
31 changes: 26 additions & 5 deletions sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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: "
Expand All @@ -87,21 +91,38 @@ 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,
("Error VMBUS: malloc failed to allocate"
" 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;

/*
Expand Down
10 changes: 10 additions & 0 deletions sys/dev/hyperv/vmbus/hv_vmbus_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit b706b38

Please sign in to comment.