svn commit: r294886 - in head/sys/dev/hyperv: include vmbus
Sepherosa Ziehau
sephe at FreeBSD.org
Wed Jan 27 03:53:32 UTC 2016
Author: sephe
Date: Wed Jan 27 03:53:30 2016
New Revision: 294886
URL: https://svnweb.freebsd.org/changeset/base/294886
Log:
hyperv/vmbus: Event handling code refactor.
- Use taskqueue instead of swi for event handling.
- Scan the interrupt flags in filter
- Disable ringbuffer interrupt mask in filter to ensure no unnecessary
interrupts.
Submitted by: Jun Su <junsu microsoft com>
Reviewed by: adrian, sephe, Dexuan <decui microsoft com>
Approved by: adrian (mentor)
MFC after: 2 weeks
Sponsored by: Microsoft OSTC
Differential Revision: https://reviews.freebsd.org/D4920
Modified:
head/sys/dev/hyperv/include/hyperv.h
head/sys/dev/hyperv/vmbus/hv_channel.c
head/sys/dev/hyperv/vmbus/hv_connection.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/include/hyperv.h
==============================================================================
--- head/sys/dev/hyperv/include/hyperv.h Wed Jan 27 03:51:24 2016 (r294885)
+++ head/sys/dev/hyperv/include/hyperv.h Wed Jan 27 03:53:30 2016 (r294886)
@@ -755,6 +755,8 @@ typedef struct hv_vmbus_channel {
struct mtx inbound_lock;
+ struct taskqueue * rxq;
+ struct task channel_task;
hv_vmbus_pfn_channel_callback on_channel_callback;
void* channel_callback_context;
Modified: head/sys/dev/hyperv/vmbus/hv_channel.c
==============================================================================
--- head/sys/dev/hyperv/vmbus/hv_channel.c Wed Jan 27 03:51:24 2016 (r294885)
+++ head/sys/dev/hyperv/vmbus/hv_channel.c Wed Jan 27 03:53:30 2016 (r294886)
@@ -51,6 +51,7 @@ static int vmbus_channel_create_gpadl_h
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 @@ hv_vmbus_channel_open(
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 @@ static void
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 @@ hv_vmbus_channel_close_internal(hv_vmbus
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 @@ hv_vmbus_channel_recv_packet_raw(
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);
+}
Modified: head/sys/dev/hyperv/vmbus/hv_connection.c
==============================================================================
--- head/sys/dev/hyperv/vmbus/hv_connection.c Wed Jan 27 03:51:24 2016 (r294885)
+++ head/sys/dev/hyperv/vmbus/hv_connection.c Wed Jan 27 03:53:30 2016 (r294886)
@@ -335,78 +335,12 @@ hv_vmbus_disconnect(void) {
}
/**
- * 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_on_events(void *arg)
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 @@ hv_vmbus_on_events(void *arg)
*/
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);
}
}
}
Modified: head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c
==============================================================================
--- head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c Wed Jan 27 03:51:24 2016 (r294885)
+++ head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c Wed Jan 27 03:53:30 2016 (r294886)
@@ -177,7 +177,7 @@ hv_vmbus_isr(struct trapframe *frame)
(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 @@ hv_vmbus_isr(struct trapframe *frame)
* 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 @@ hv_vmbus_isr(struct trapframe *frame)
return (FILTER_HANDLED);
}
-uint32_t hv_vmbus_swintr_event_cpu[MAXCPU];
u_long *hv_vmbus_intr_cpu[MAXCPU];
void
@@ -472,6 +471,7 @@ vmbus_bus_init(void)
{
int i, j, n, ret;
char buf[MAXCOMLEN + 1];
+ cpuset_t cpu_mask;
if (vmbus_inited)
return (0);
@@ -508,10 +508,7 @@ vmbus_bus_init(void)
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 @@ vmbus_bus_init(void)
*/
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 @@ vmbus_bus_init(void)
*/
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 @@ vmbus_bus_init(void)
}
/*
- * 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 @@ vmbus_bus_init(void)
* 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 @@ vmbus_bus_exit(void)
/* 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);
Modified: head/sys/dev/hyperv/vmbus/hv_vmbus_priv.h
==============================================================================
--- head/sys/dev/hyperv/vmbus/hv_vmbus_priv.h Wed Jan 27 03:51:24 2016 (r294885)
+++ head/sys/dev/hyperv/vmbus/hv_vmbus_priv.h Wed Jan 27 03:53:30 2016 (r294886)
@@ -202,9 +202,8 @@ typedef struct {
* 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_connect(void);
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
More information about the svn-src-head
mailing list