Index: head/sys/dev/hyperv/include/hyperv.h =================================================================== --- head/sys/dev/hyperv/include/hyperv.h +++ head/sys/dev/hyperv/include/hyperv.h @@ -755,6 +755,8 @@ struct mtx inbound_lock; + struct taskqueue * rxq; + struct task channel_task; hv_vmbus_pfn_channel_callback on_channel_callback; void* channel_callback_context; Index: head/sys/dev/hyperv/vmbus/hv_channel.c =================================================================== --- head/sys/dev/hyperv/vmbus/hv_channel.c +++ head/sys/dev/hyperv/vmbus/hv_channel.c @@ -51,6 +51,7 @@ uint32_t* message_count); static void vmbus_channel_set_event(hv_vmbus_channel* channel); +static void VmbusProcessChannelEvent(void* channel, int pending); /** * @brief Trigger an event notification on the specified channel @@ -114,6 +115,9 @@ new_channel->on_channel_callback = pfn_on_channel_callback; new_channel->channel_callback_context = context; + new_channel->rxq = hv_vmbus_g_context.hv_event_queue[new_channel->target_cpu]; + TASK_INIT(&new_channel->channel_task, 0, VmbusProcessChannelEvent, new_channel); + /* Allocate the ring buffer */ out = contigmalloc((send_ring_buffer_size + recv_ring_buffer_size), M_DEVBUF, M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); @@ -517,6 +521,7 @@ hv_vmbus_channel_close_internal(hv_vmbus_channel *channel) { int ret = 0; + struct taskqueue *rxq = channel->rxq; hv_vmbus_channel_close_channel* msg; hv_vmbus_channel_msg_info* info; @@ -524,6 +529,11 @@ channel->sc_creation_callback = NULL; /* + * set rxq to NULL to avoid more requests be scheduled + */ + channel->rxq = NULL; + taskqueue_drain(rxq, &channel->channel_task); + /* * Grab the lock to prevent race condition when a packet received * and unloading driver is in the process. */ @@ -876,3 +886,67 @@ return (0); } + + +/** + * Process a channel event notification + */ +static void +VmbusProcessChannelEvent(void* context, int pending) +{ + void* arg; + uint32_t bytes_to_read; + hv_vmbus_channel* channel = (hv_vmbus_channel*)context; + boolean_t is_batched_reading; + + /** + * Find the channel based on this relid and invokes + * the channel callback to process the event + */ + + if (channel == NULL) { + return; + } + /** + * To deal with the race condition where we might + * receive a packet while the relevant driver is + * being unloaded, dispatch the callback while + * holding the channel lock. The unloading driver + * will acquire the same channel lock to set the + * callback to NULL. This closes the window. + */ + + /* + * Disable the lock due to newly added WITNESS check in r277723. + * Will seek other way to avoid race condition. + * -- whu + */ + // mtx_lock(&channel->inbound_lock); + if (channel->on_channel_callback != NULL) { + arg = channel->channel_callback_context; + is_batched_reading = channel->batched_reading; + /* + * Optimize host to guest signaling by ensuring: + * 1. While reading the channel, we disable interrupts from + * host. + * 2. Ensure that we process all posted messages from the host + * before returning from this callback. + * 3. Once we return, enable signaling from the host. Once this + * state is set we check to see if additional packets are + * available to read. In this case we repeat the process. + */ + do { + if (is_batched_reading) + hv_ring_buffer_read_begin(&channel->inbound); + + channel->on_channel_callback(arg); + + if (is_batched_reading) + bytes_to_read = + hv_ring_buffer_read_end(&channel->inbound); + else + bytes_to_read = 0; + } while (is_batched_reading && (bytes_to_read != 0)); + } + // mtx_unlock(&channel->inbound_lock); +} Index: head/sys/dev/hyperv/vmbus/hv_connection.c =================================================================== --- head/sys/dev/hyperv/vmbus/hv_connection.c +++ head/sys/dev/hyperv/vmbus/hv_connection.c @@ -335,78 +335,12 @@ } /** - * Process a channel event notification - */ -static void -VmbusProcessChannelEvent(uint32_t relid) -{ - void* arg; - uint32_t bytes_to_read; - hv_vmbus_channel* channel; - boolean_t is_batched_reading; - - /** - * Find the channel based on this relid and invokes - * the channel callback to process the event - */ - - channel = hv_vmbus_g_connection.channels[relid]; - - if (channel == NULL) { - return; - } - /** - * To deal with the race condition where we might - * receive a packet while the relevant driver is - * being unloaded, dispatch the callback while - * holding the channel lock. The unloading driver - * will acquire the same channel lock to set the - * callback to NULL. This closes the window. - */ - - /* - * Disable the lock due to newly added WITNESS check in r277723. - * Will seek other way to avoid race condition. - * -- whu - */ - // mtx_lock(&channel->inbound_lock); - if (channel->on_channel_callback != NULL) { - arg = channel->channel_callback_context; - is_batched_reading = channel->batched_reading; - /* - * Optimize host to guest signaling by ensuring: - * 1. While reading the channel, we disable interrupts from - * host. - * 2. Ensure that we process all posted messages from the host - * before returning from this callback. - * 3. Once we return, enable signaling from the host. Once this - * state is set we check to see if additional packets are - * available to read. In this case we repeat the process. - */ - do { - if (is_batched_reading) - hv_ring_buffer_read_begin(&channel->inbound); - - channel->on_channel_callback(arg); - - if (is_batched_reading) - bytes_to_read = - hv_ring_buffer_read_end(&channel->inbound); - else - bytes_to_read = 0; - } while (is_batched_reading && (bytes_to_read != 0)); - } - // mtx_unlock(&channel->inbound_lock); -} - -/** * Handler for events */ void -hv_vmbus_on_events(void *arg) +hv_vmbus_on_events(int cpu) { int bit; - int cpu; int dword; void *page_addr; uint32_t* recv_interrupt_page = NULL; @@ -415,7 +349,6 @@ hv_vmbus_synic_event_flags *event; /* int maxdword = PAGE_SIZE >> 3; */ - cpu = (int)(long)arg; KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: " "cpu out of range!")); @@ -457,8 +390,14 @@ */ continue; } else { - VmbusProcessChannelEvent(rel_id); - + hv_vmbus_channel * channel = hv_vmbus_g_connection.channels[rel_id]; + /* if channel is closed or closing */ + if (channel == NULL || channel->rxq == NULL) + continue; + + if (channel->batched_reading) + hv_ring_buffer_read_begin(&channel->inbound); + taskqueue_enqueue_fast(channel->rxq, &channel->channel_task); } } } Index: head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c =================================================================== --- head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c +++ head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c @@ -177,7 +177,7 @@ (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) { /* Since we are a child, we only need to check bit 0 */ if (synch_test_and_clear_bit(0, &event->flags32[0])) { - swi_sched(hv_vmbus_g_context.event_swintr[cpu], 0); + hv_vmbus_on_events(cpu); } } else { /* @@ -187,7 +187,7 @@ * Directly schedule the event software interrupt on * current cpu. */ - swi_sched(hv_vmbus_g_context.event_swintr[cpu], 0); + hv_vmbus_on_events(cpu); } /* Check if there are actual msgs to be process */ @@ -225,7 +225,6 @@ return (FILTER_HANDLED); } -uint32_t hv_vmbus_swintr_event_cpu[MAXCPU]; u_long *hv_vmbus_intr_cpu[MAXCPU]; void @@ -472,6 +471,7 @@ { int i, j, n, ret; char buf[MAXCOMLEN + 1]; + cpuset_t cpu_mask; if (vmbus_inited) return (0); @@ -508,10 +508,7 @@ setup_args.vector = hv_vmbus_g_context.hv_cb_vector; CPU_FOREACH(j) { - hv_vmbus_swintr_event_cpu[j] = 0; - hv_vmbus_g_context.hv_event_intr_event[j] = NULL; hv_vmbus_g_context.hv_msg_intr_event[j] = NULL; - hv_vmbus_g_context.event_swintr[j] = NULL; hv_vmbus_g_context.msg_swintr[j] = NULL; snprintf(buf, sizeof(buf), "cpu%d:hyperv", j); @@ -526,6 +523,20 @@ */ CPU_FOREACH(j) { /* + * Setup taskqueue to handle events + */ + hv_vmbus_g_context.hv_event_queue[j] = taskqueue_create_fast("hyperv event", M_WAITOK, + taskqueue_thread_enqueue, &hv_vmbus_g_context.hv_event_queue[j]); + if (hv_vmbus_g_context.hv_event_queue[j] == NULL) { + if (bootverbose) + printf("VMBUS: failed to setup taskqueue\n"); + goto cleanup1; + } + CPU_SETOF(j, &cpu_mask); + taskqueue_start_threads_cpuset(&hv_vmbus_g_context.hv_event_queue[j], 1, PI_NET, &cpu_mask, + "hvevent%d", j); + + /* * Setup software interrupt thread and handler for msg handling. */ ret = swi_add(&hv_vmbus_g_context.hv_msg_intr_event[j], @@ -543,7 +554,7 @@ */ ret = intr_event_bind(hv_vmbus_g_context.hv_msg_intr_event[j], j); - if (ret) { + if (ret) { if(bootverbose) printf("VMBUS: failed to bind msg swi thread " "to cpu %d\n", j); @@ -551,20 +562,6 @@ } /* - * Setup software interrupt thread and handler for - * event handling. - */ - ret = swi_add(&hv_vmbus_g_context.hv_event_intr_event[j], - "hv_event", hv_vmbus_on_events, (void *)(long)j, - SWI_CLOCK, 0, &hv_vmbus_g_context.event_swintr[j]); - if (ret) { - if(bootverbose) - printf("VMBUS: failed to setup event swi for " - "cpu %d\n", j); - goto cleanup1; - } - - /* * Prepare the per cpu msg and event pages to be called on each cpu. */ for(i = 0; i < 2; i++) { @@ -607,12 +604,11 @@ * remove swi and vmbus callback vector; */ CPU_FOREACH(j) { + if (hv_vmbus_g_context.hv_event_queue[j] != NULL) + taskqueue_free(hv_vmbus_g_context.hv_event_queue[j]); if (hv_vmbus_g_context.msg_swintr[j] != NULL) swi_remove(hv_vmbus_g_context.msg_swintr[j]); - if (hv_vmbus_g_context.event_swintr[j] != NULL) - swi_remove(hv_vmbus_g_context.event_swintr[j]); hv_vmbus_g_context.hv_msg_intr_event[j] = NULL; - hv_vmbus_g_context.hv_event_intr_event[j] = NULL; } vmbus_vector_free(hv_vmbus_g_context.hv_cb_vector); @@ -677,12 +673,11 @@ /* remove swi */ CPU_FOREACH(i) { + if (hv_vmbus_g_context.hv_event_queue[i] != NULL) + taskqueue_free(hv_vmbus_g_context.hv_event_queue[i]); if (hv_vmbus_g_context.msg_swintr[i] != NULL) swi_remove(hv_vmbus_g_context.msg_swintr[i]); - if (hv_vmbus_g_context.event_swintr[i] != NULL) - swi_remove(hv_vmbus_g_context.event_swintr[i]); hv_vmbus_g_context.hv_msg_intr_event[i] = NULL; - hv_vmbus_g_context.hv_event_intr_event[i] = NULL; } vmbus_vector_free(hv_vmbus_g_context.hv_cb_vector); Index: head/sys/dev/hyperv/vmbus/hv_vmbus_priv.h =================================================================== --- head/sys/dev/hyperv/vmbus/hv_vmbus_priv.h +++ head/sys/dev/hyperv/vmbus/hv_vmbus_priv.h @@ -202,9 +202,8 @@ * Each cpu has its own software interrupt handler for channel * event and msg handling. */ - struct intr_event *hv_event_intr_event[MAXCPU]; + struct taskqueue *hv_event_queue[MAXCPU]; struct intr_event *hv_msg_intr_event[MAXCPU]; - void *event_swintr[MAXCPU]; void *msg_swintr[MAXCPU]; /* * Host use this vector to intrrupt guest for vmbus channel @@ -717,7 +716,7 @@ int hv_vmbus_disconnect(void); int hv_vmbus_post_message(void *buffer, size_t buf_size); int hv_vmbus_set_event(hv_vmbus_channel *channel); -void hv_vmbus_on_events(void *); +void hv_vmbus_on_events(int cpu); /** * Event Timer interfaces