vmbus: change to per channel tasklet
authorStephen Hemminger <stephen@networkplumber.org>
Sun, 12 Feb 2017 06:02:20 +0000 (23:02 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 14 Feb 2017 18:20:35 +0000 (10:20 -0800)
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>
drivers/hv/channel.c
drivers/hv/channel_mgmt.c
drivers/hv/connection.c
drivers/hv/hv.c
drivers/hv/hyperv_vmbus.h
drivers/hv/vmbus_drv.c
include/linux/hyperv.h

index 789c75f6df26f61d027545e8da855f8d20dd792c..18cc1c78260d42c0c8fb1fda0fed11e38d590977 100644 (file)
@@ -530,7 +530,7 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
        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
         * the former is accessing channel->inbound.ring_buffer, the latter
         * could be freeing the ring_buffer pages.
index 579ad2560a39525d60ed24192036ff1b514fbe2a..2f6270d76b797b786d1cfb4d33bfdace22b6b309 100644 (file)
@@ -339,6 +339,9 @@ static struct vmbus_channel *alloc_channel(void)
        INIT_LIST_HEAD(&channel->sc_list);
        INIT_LIST_HEAD(&channel->percpu_list);
 
+       tasklet_init(&channel->callback_event,
+                    vmbus_on_event, (unsigned long)channel);
+
        return channel;
 }
 
@@ -347,6 +350,7 @@ static struct vmbus_channel *alloc_channel(void)
  */
 static void free_channel(struct vmbus_channel *channel)
 {
+       tasklet_kill(&channel->callback_event);
        kfree(channel);
 }
 
@@ -380,21 +384,15 @@ static void vmbus_release_relid(u32 relid)
 
 void hv_event_tasklet_disable(struct vmbus_channel *channel)
 {
-       struct hv_per_cpu_context *hv_cpu;
-
-       hv_cpu = per_cpu_ptr(hv_context.cpu_context, channel->target_cpu);
-       tasklet_disable(&hv_cpu->event_dpc);
+       tasklet_disable(&channel->callback_event);
 }
 
 void hv_event_tasklet_enable(struct vmbus_channel *channel)
 {
-       struct hv_per_cpu_context *hv_cpu;
-
-       hv_cpu = per_cpu_ptr(hv_context.cpu_context, channel->target_cpu);
-       tasklet_enable(&hv_cpu->event_dpc);
+       tasklet_enable(&channel->callback_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)
index 158f12823baf4c678873d5d8e34fc2f5b5a47c51..27e72dc07e12c7a501470742340bf4a33a5109be 100644 (file)
@@ -259,29 +259,6 @@ void vmbus_disconnect(void)
        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
  * child relative id (ie channel id)
@@ -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;
        bool read_state;
        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
         * is no driver handling the device. An unloading driver
@@ -344,7 +312,6 @@ static void process_chn_event(u32 relid)
         * Thus, checking and invoking the driver specific callback takes
         * care of orderly unloading of the driver.
         */
-
        if (channel->onchannel_callback != NULL) {
                arg = channel->channel_callback_context;
                read_state = channel->batched_reading;
@@ -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
  */
index 9fc2355a60ea6791847b24f2811a8173d33b4dcc..665a64f1611e31fe3650f6d030aa373ca1301c03 100644 (file)
@@ -156,8 +156,6 @@ int hv_synic_alloc(void)
                        = per_cpu_ptr(hv_context.cpu_context, 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,
                             vmbus_on_msg_dpc, (unsigned long) hv_cpu);
 
index c8ce9ab2e16a4327a5d129f62f63c87b2d2c9195..558a798c407c90ab7353016cdfc2c18771c4c988 100644 (file)
@@ -206,7 +206,6 @@ struct hv_per_cpu_context {
         * we will manage the tasklet that handles events messages on a per CPU
         * basis.
         */
-       struct tasklet_struct event_dpc;
        struct tasklet_struct msg_dpc;
 
        /*
index cf8540c1df4ac4b4cc867f50d7a198a1d785b3e1..eaf1a10b02453d9b361fb74c65f7e0f1bb90b9ce 100644 (file)
@@ -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);
index c9b6d533958f4b7a08a6dc2581ba3acf13cb17a7..69afc9337c0daeb4b1e6f86900f09090b44a772f 100644 (file)
@@ -35,7 +35,7 @@
 #include <linux/completion.h>
 #include <linux/device.h>
 #include <linux/mod_devicetable.h>
-
+#include <linux/interrupt.h>
 
 #define MAX_PAGE_BUFFER_COUNT                          32
 #define MAX_MULTIPAGE_BUFFER_COUNT                     32 /* 128K */
@@ -743,6 +743,7 @@ struct vmbus_channel {
        struct vmbus_close_msg close_msg;
 
        /* Channel callback's invoked in softirq context */
+       struct tasklet_struct callback_event;
        void (*onchannel_callback)(void *context);
        void *channel_callback_context;