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:
parent
582d05198e
commit
0eff2e2ea1
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=294886
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user