summaryrefslogtreecommitdiff
path: root/drivers/hv/vmbus_drv.c
diff options
context:
space:
mode:
authorStephen Hemminger <stephen@networkplumber.org>2017-02-12 09:02:20 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-02-14 21:20:35 +0300
commit631e63a9f346cb657761ae22138f294718696501 (patch)
tree9d207333ca07e73282e8c7e8a4c36cf73f0d7b63 /drivers/hv/vmbus_drv.c
parent37cdd991fac810a727cd285629d1640fcf53cd19 (diff)
downloadlinux-631e63a9f346cb657761ae22138f294718696501.tar.xz
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: Stephen Hemminger <sthemmin@microsoft.com> Signed-off-by: K. Y. Srinivasan <kys@microsoft.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/hv/vmbus_drv.c')
-rw-r--r--drivers/hv/vmbus_drv.c58
1 files changed, 51 insertions, 7 deletions
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index cf8540c1df4a..eaf1a10b0245 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -885,6 +885,56 @@ msg_handled:
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)
{
struct hv_per_cpu_context *hv_cpu
@@ -922,8 +972,7 @@ static void vmbus_isr(void)
}
if (handled)
- tasklet_schedule(&hv_cpu->event_dpc);
-
+ vmbus_chan_sched(hv_cpu);
page_addr = hv_cpu->synic_message_page;
msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT;
@@ -1536,12 +1585,7 @@ static void __exit vmbus_exit(void)
&hyperv_panic_block);
}
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);
hv_synic_free();
acpi_bus_unregister_driver(&vmbus_acpi_driver);