Commit 631e63a9 authored by Stephen Hemminger's avatar Stephen Hemminger Committed by Greg Kroah-Hartman

vmbus: change to per channel tasklet

Make the event handling tasklet per channel rather than per-cpu.
This allows for better fairness when getting lots of data on the same
cpu.
Signed-off-by: default avatarStephen Hemminger <sthemmin@microsoft.com>
Signed-off-by: default avatarK. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 37cdd991
...@@ -530,7 +530,7 @@ static int vmbus_close_internal(struct vmbus_channel *channel) ...@@ -530,7 +530,7 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
int ret; int ret;
/* /*
* process_chn_event(), running in the tasklet, can race * vmbus_on_event(), running in the tasklet, can race
* with vmbus_close_internal() in the case of SMP guest, e.g., when * with vmbus_close_internal() in the case of SMP guest, e.g., when
* the former is accessing channel->inbound.ring_buffer, the latter * the former is accessing channel->inbound.ring_buffer, the latter
* could be freeing the ring_buffer pages. * could be freeing the ring_buffer pages.
......
...@@ -339,6 +339,9 @@ static struct vmbus_channel *alloc_channel(void) ...@@ -339,6 +339,9 @@ static struct vmbus_channel *alloc_channel(void)
INIT_LIST_HEAD(&channel->sc_list); INIT_LIST_HEAD(&channel->sc_list);
INIT_LIST_HEAD(&channel->percpu_list); INIT_LIST_HEAD(&channel->percpu_list);
tasklet_init(&channel->callback_event,
vmbus_on_event, (unsigned long)channel);
return channel; return channel;
} }
...@@ -347,6 +350,7 @@ static struct vmbus_channel *alloc_channel(void) ...@@ -347,6 +350,7 @@ static struct vmbus_channel *alloc_channel(void)
*/ */
static void free_channel(struct vmbus_channel *channel) static void free_channel(struct vmbus_channel *channel)
{ {
tasklet_kill(&channel->callback_event);
kfree(channel); kfree(channel);
} }
...@@ -380,21 +384,15 @@ static void vmbus_release_relid(u32 relid) ...@@ -380,21 +384,15 @@ static void vmbus_release_relid(u32 relid)
void hv_event_tasklet_disable(struct vmbus_channel *channel) void hv_event_tasklet_disable(struct vmbus_channel *channel)
{ {
struct hv_per_cpu_context *hv_cpu; tasklet_disable(&channel->callback_event);
hv_cpu = per_cpu_ptr(hv_context.cpu_context, channel->target_cpu);
tasklet_disable(&hv_cpu->event_dpc);
} }
void hv_event_tasklet_enable(struct vmbus_channel *channel) void hv_event_tasklet_enable(struct vmbus_channel *channel)
{ {
struct hv_per_cpu_context *hv_cpu; tasklet_enable(&channel->callback_event);
hv_cpu = per_cpu_ptr(hv_context.cpu_context, channel->target_cpu);
tasklet_enable(&hv_cpu->event_dpc);
/* In case there is any pending event */ /* In case there is any pending event */
tasklet_schedule(&hv_cpu->event_dpc); tasklet_schedule(&channel->callback_event);
} }
void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid) void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
......
...@@ -259,29 +259,6 @@ void vmbus_disconnect(void) ...@@ -259,29 +259,6 @@ void vmbus_disconnect(void)
vmbus_connection.monitor_pages[1] = NULL; vmbus_connection.monitor_pages[1] = NULL;
} }
/*
* Map the given relid to the corresponding channel based on the
* per-cpu list of channels that have been affinitized to this CPU.
* This will be used in the channel callback path as we can do this
* mapping in a lock-free fashion.
*/
static struct vmbus_channel *pcpu_relid2channel(u32 relid)
{
struct hv_per_cpu_context *hv_cpu
= this_cpu_ptr(hv_context.cpu_context);
struct vmbus_channel *found_channel = NULL;
struct vmbus_channel *channel;
list_for_each_entry(channel, &hv_cpu->chan_list, percpu_list) {
if (channel->offermsg.child_relid == relid) {
found_channel = channel;
break;
}
}
return found_channel;
}
/* /*
* relid2channel - Get the channel object given its * relid2channel - Get the channel object given its
* child relative id (ie channel id) * child relative id (ie channel id)
...@@ -318,24 +295,15 @@ struct vmbus_channel *relid2channel(u32 relid) ...@@ -318,24 +295,15 @@ struct vmbus_channel *relid2channel(u32 relid)
} }
/* /*
* process_chn_event - Process a channel event notification * vmbus_on_event - Process a channel event notification
*/ */
static void process_chn_event(u32 relid) void vmbus_on_event(unsigned long data)
{ {
struct vmbus_channel *channel; struct vmbus_channel *channel = (void *) data;
void *arg; void *arg;
bool read_state; bool read_state;
u32 bytes_to_read; u32 bytes_to_read;
/*
* Find the channel based on this relid and invokes the
* channel callback to process the event
*/
channel = pcpu_relid2channel(relid);
if (!channel)
return;
/* /*
* A channel once created is persistent even when there * A channel once created is persistent even when there
* is no driver handling the device. An unloading driver * is no driver handling the device. An unloading driver
...@@ -344,7 +312,6 @@ static void process_chn_event(u32 relid) ...@@ -344,7 +312,6 @@ static void process_chn_event(u32 relid)
* Thus, checking and invoking the driver specific callback takes * Thus, checking and invoking the driver specific callback takes
* care of orderly unloading of the driver. * care of orderly unloading of the driver.
*/ */
if (channel->onchannel_callback != NULL) { if (channel->onchannel_callback != NULL) {
arg = channel->channel_callback_context; arg = channel->channel_callback_context;
read_state = channel->batched_reading; read_state = channel->batched_reading;
...@@ -372,45 +339,6 @@ static void process_chn_event(u32 relid) ...@@ -372,45 +339,6 @@ static void process_chn_event(u32 relid)
} }
} }
/*
* vmbus_on_event - Handler for events
*/
void vmbus_on_event(unsigned long data)
{
struct hv_per_cpu_context *hv_cpu = (void *)data;
unsigned long *recv_int_page;
u32 maxbits, relid;
if (vmbus_proto_version < VERSION_WIN8) {
maxbits = MAX_NUM_CHANNELS_SUPPORTED;
recv_int_page = vmbus_connection.recv_int_page;
} else {
/*
* When the host is win8 and beyond, the event page
* can be directly checked to get the id of the channel
* that has the interrupt pending.
*/
void *page_addr = hv_cpu->synic_event_page;
union hv_synic_event_flags *event
= (union hv_synic_event_flags *)page_addr +
VMBUS_MESSAGE_SINT;
maxbits = HV_EVENT_FLAGS_COUNT;
recv_int_page = event->flags;
}
if (unlikely(!recv_int_page))
return;
for_each_set_bit(relid, recv_int_page, maxbits) {
if (sync_test_and_clear_bit(relid, recv_int_page)) {
/* Special case - vmbus channel protocol msg */
if (relid != 0)
process_chn_event(relid);
}
}
}
/* /*
* vmbus_post_msg - Send a msg on the vmbus's message connection * vmbus_post_msg - Send a msg on the vmbus's message connection
*/ */
......
...@@ -156,8 +156,6 @@ int hv_synic_alloc(void) ...@@ -156,8 +156,6 @@ int hv_synic_alloc(void)
= per_cpu_ptr(hv_context.cpu_context, cpu); = per_cpu_ptr(hv_context.cpu_context, cpu);
memset(hv_cpu, 0, sizeof(*hv_cpu)); memset(hv_cpu, 0, sizeof(*hv_cpu));
tasklet_init(&hv_cpu->event_dpc,
vmbus_on_event, (unsigned long) hv_cpu);
tasklet_init(&hv_cpu->msg_dpc, tasklet_init(&hv_cpu->msg_dpc,
vmbus_on_msg_dpc, (unsigned long) hv_cpu); vmbus_on_msg_dpc, (unsigned long) hv_cpu);
......
...@@ -206,7 +206,6 @@ struct hv_per_cpu_context { ...@@ -206,7 +206,6 @@ struct hv_per_cpu_context {
* we will manage the tasklet that handles events messages on a per CPU * we will manage the tasklet that handles events messages on a per CPU
* basis. * basis.
*/ */
struct tasklet_struct event_dpc;
struct tasklet_struct msg_dpc; struct tasklet_struct msg_dpc;
/* /*
......
...@@ -885,6 +885,56 @@ void vmbus_on_msg_dpc(unsigned long data) ...@@ -885,6 +885,56 @@ void vmbus_on_msg_dpc(unsigned long data)
vmbus_signal_eom(msg, message_type); vmbus_signal_eom(msg, message_type);
} }
/*
* Schedule all channels with events pending
*/
static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu)
{
unsigned long *recv_int_page;
u32 maxbits, relid;
if (vmbus_proto_version < VERSION_WIN8) {
maxbits = MAX_NUM_CHANNELS_SUPPORTED;
recv_int_page = vmbus_connection.recv_int_page;
} else {
/*
* When the host is win8 and beyond, the event page
* can be directly checked to get the id of the channel
* that has the interrupt pending.
*/
void *page_addr = hv_cpu->synic_event_page;
union hv_synic_event_flags *event
= (union hv_synic_event_flags *)page_addr +
VMBUS_MESSAGE_SINT;
maxbits = HV_EVENT_FLAGS_COUNT;
recv_int_page = event->flags;
}
if (unlikely(!recv_int_page))
return;
for_each_set_bit(relid, recv_int_page, maxbits) {
struct vmbus_channel *channel;
if (!sync_test_and_clear_bit(relid, recv_int_page))
continue;
/* Special case - vmbus channel protocol msg */
if (relid == 0)
continue;
/* Find channel based on relid */
list_for_each_entry(channel, &hv_cpu->chan_list, percpu_list) {
if (channel->offermsg.child_relid == relid) {
tasklet_schedule(&channel->callback_event);
break;
}
}
}
}
static void vmbus_isr(void) static void vmbus_isr(void)
{ {
struct hv_per_cpu_context *hv_cpu struct hv_per_cpu_context *hv_cpu
...@@ -922,8 +972,7 @@ static void vmbus_isr(void) ...@@ -922,8 +972,7 @@ static void vmbus_isr(void)
} }
if (handled) if (handled)
tasklet_schedule(&hv_cpu->event_dpc); vmbus_chan_sched(hv_cpu);
page_addr = hv_cpu->synic_message_page; page_addr = hv_cpu->synic_message_page;
msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT;
...@@ -1536,12 +1585,7 @@ static void __exit vmbus_exit(void) ...@@ -1536,12 +1585,7 @@ static void __exit vmbus_exit(void)
&hyperv_panic_block); &hyperv_panic_block);
} }
bus_unregister(&hv_bus); bus_unregister(&hv_bus);
for_each_online_cpu(cpu) {
struct hv_per_cpu_context *hv_cpu
= per_cpu_ptr(hv_context.cpu_context, cpu);
tasklet_kill(&hv_cpu->event_dpc);
}
cpuhp_remove_state(hyperv_cpuhp_online); cpuhp_remove_state(hyperv_cpuhp_online);
hv_synic_free(); hv_synic_free();
acpi_bus_unregister_driver(&vmbus_acpi_driver); acpi_bus_unregister_driver(&vmbus_acpi_driver);
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/interrupt.h>
#define MAX_PAGE_BUFFER_COUNT 32 #define MAX_PAGE_BUFFER_COUNT 32
#define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */ #define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */
...@@ -743,6 +743,7 @@ struct vmbus_channel { ...@@ -743,6 +743,7 @@ struct vmbus_channel {
struct vmbus_close_msg close_msg; struct vmbus_close_msg close_msg;
/* Channel callback's invoked in softirq context */ /* Channel callback's invoked in softirq context */
struct tasklet_struct callback_event;
void (*onchannel_callback)(void *context); void (*onchannel_callback)(void *context);
void *channel_callback_context; void *channel_callback_context;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment