1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-16 10:20:30 +00:00

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
This commit is contained in:
Sepherosa Ziehau 2016-01-27 03:53:30 +00:00
parent 582d05198e
commit 0eff2e2ea1
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=294886
5 changed files with 108 additions and 99 deletions

View File

@ -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;

View File

@ -51,6 +51,7 @@ static int vmbus_channel_create_gpadl_header(
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,12 +521,18 @@ 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;
channel->state = HV_CHANNEL_OPEN_STATE;
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);
}

View File

@ -334,79 +334,13 @@ hv_vmbus_disconnect(void) {
return (ret);
}
/**
* 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);
}
}
}

View File

@ -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);
@ -525,6 +522,20 @@ vmbus_bus_init(void)
* Per cpu setup.
*/
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.
*/
@ -543,27 +554,13 @@ 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);
goto cleanup1;
}
/*
* 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.
*/
@ -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);

View File

@ -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