vmbus: add direct isr callback mode
authorStephen Hemminger <stephen@networkplumber.org>
Sun, 12 Feb 2017 06:02:21 +0000 (23:02 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 14 Feb 2017 18:20:35 +0000 (10:20 -0800)
Change the simple boolean batched_reading into a tri-value.
For future NAPI support in netvsc driver, the callback needs to
occur directly in interrupt handler.

Batched mode is also changed to disable host interrupts immediately
in interrupt routine (to avoid unnecessary host signals), and the
tasklet is rescheduled if more data is detected.

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_mgmt.c
drivers/hv/connection.c
drivers/hv/hv_util.c
drivers/hv/vmbus_drv.c
drivers/uio/uio_hv_generic.c
include/linux/hyperv.h

index 2f6270d76b797b786d1cfb4d33bfdace22b6b309..b2bb5aafaa2fb60fe55a94081a6c90646f1c9560 100644 (file)
@@ -819,13 +819,6 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
                return;
        }
 
-       /*
-        * By default we setup state to enable batched
-        * reading. A specific service can choose to
-        * disable this prior to opening the channel.
-        */
-       newchannel->batched_reading = true;
-
        /*
         * Setup state for signalling the host.
         */
index 27e72dc07e12c7a501470742340bf4a33a5109be..a8366fec14581f795c046dcd47baef7a56f20692 100644 (file)
@@ -300,9 +300,7 @@ struct vmbus_channel *relid2channel(u32 relid)
 void vmbus_on_event(unsigned long data)
 {
        struct vmbus_channel *channel = (void *) data;
-       void *arg;
-       bool read_state;
-       u32 bytes_to_read;
+       void (*callback_fn)(void *);
 
        /*
         * A channel once created is persistent even when there
@@ -312,9 +310,13 @@ void vmbus_on_event(unsigned long data)
         * 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;
+       callback_fn = READ_ONCE(channel->onchannel_callback);
+       if (unlikely(callback_fn == NULL))
+               return;
+
+       (*callback_fn)(channel->channel_callback_context);
+
+       if (channel->callback_mode == HV_CALL_BATCHED) {
                /*
                 * This callback reads the messages sent by the host.
                 * We can optimize host to guest signaling by ensuring:
@@ -326,16 +328,11 @@ void vmbus_on_event(unsigned long data)
                 *    state is set we check to see if additional packets are
                 *    available to read. In this case we repeat the process.
                 */
+               if (hv_end_read(&channel->inbound) != 0) {
+                       hv_begin_read(&channel->inbound);
 
-               do {
-                       if (read_state)
-                               hv_begin_read(&channel->inbound);
-                       channel->onchannel_callback(arg);
-                       if (read_state)
-                               bytes_to_read = hv_end_read(&channel->inbound);
-                       else
-                               bytes_to_read = 0;
-               } while (read_state && (bytes_to_read != 0));
+                       tasklet_schedule(&channel->callback_event);
+               }
        }
 }
 
index 098cd3dc7db2d39f0393f424f1307c1211df8d19..3042eaa13062bbdfbdba853521b7632d35e619e8 100644 (file)
@@ -435,8 +435,7 @@ static int util_probe(struct hv_device *dev,
         * Turn off batched reading for all util drivers before we open the
         * channel.
         */
-
-       set_channel_read_state(dev->channel, false);
+       set_channel_read_mode(dev->channel, HV_CALL_DIRECT);
 
        hv_set_drvdata(dev, srv);
 
index eaf1a10b02453d9b361fb74c65f7e0f1bb90b9ce..f7f6b9144b07c012c9987013a833823cc03934cb 100644 (file)
@@ -886,6 +886,18 @@ msg_handled:
 }
 
 
+/*
+ * Direct callback for channels using other deferred processing
+ */
+static void vmbus_channel_isr(struct vmbus_channel *channel)
+{
+       void (*callback_fn)(void *);
+
+       callback_fn = READ_ONCE(channel->onchannel_callback);
+       if (likely(callback_fn != NULL))
+               (*callback_fn)(channel->channel_callback_context);
+}
+
 /*
  * Schedule all channels with events pending
  */
@@ -927,9 +939,19 @@ static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu)
 
                /* 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);
+                       if (channel->offermsg.child_relid != relid)
+                               continue;
+
+                       switch (channel->callback_mode) {
+                       case HV_CALL_ISR:
+                               vmbus_channel_isr(channel);
                                break;
+
+                       case HV_CALL_BATCHED:
+                               hv_begin_read(&channel->inbound);
+                               /* fallthrough */
+                       case HV_CALL_DIRECT:
+                               tasklet_schedule(&channel->callback_event);
                        }
                }
        }
index 50958f167305317f3de1a361673cd3caf4f8f47b..48d5327d38d420d84f49d52aa9a904d2ee174d80 100644 (file)
@@ -125,7 +125,7 @@ hv_uio_probe(struct hv_device *dev,
                goto fail;
 
        dev->channel->inbound.ring_buffer->interrupt_mask = 1;
-       dev->channel->batched_reading = false;
+       set_channel_read_mode(dev->channel, HV_CALL_DIRECT);
 
        /* Fill general uio info */
        pdata->info.name = "uio_hv_generic";
index 69afc9337c0daeb4b1e6f86900f09090b44a772f..e5aac5c051f7d800a3b8cd8d4678fa3c56ac6511 100644 (file)
@@ -748,19 +748,21 @@ struct vmbus_channel {
        void *channel_callback_context;
 
        /*
-        * A channel can be marked for efficient (batched)
-        * reading:
-        * If batched_reading is set to "true", we read until the
-        * channel is empty and hold off interrupts from the host
-        * during the entire read process.
-        * If batched_reading is set to "false", the client is not
-        * going to perform batched reading.
-        *
-        * By default we will enable batched reading; specific
-        * drivers that don't want this behavior can turn it off.
+        * A channel can be marked for one of three modes of reading:
+        *   BATCHED - callback called from taslket and should read
+        *            channel until empty. Interrupts from the host
+        *            are masked while read is in process (default).
+        *   DIRECT - callback called from tasklet (softirq).
+        *   ISR - callback called in interrupt context and must
+        *         invoke its own deferred processing.
+        *         Host interrupts are disabled and must be re-enabled
+        *         when ring is empty.
         */
-
-       bool batched_reading;
+       enum hv_callback_mode {
+               HV_CALL_BATCHED,
+               HV_CALL_DIRECT,
+               HV_CALL_ISR
+       } callback_mode;
 
        bool is_dedicated_interrupt;
        struct hv_input_signal_event_buffer sig_buf;
@@ -910,9 +912,10 @@ static inline void set_channel_affinity_state(struct vmbus_channel *c,
        c->affinity_policy = policy;
 }
 
-static inline void set_channel_read_state(struct vmbus_channel *c, bool state)
+static inline void set_channel_read_mode(struct vmbus_channel *c,
+                                       enum hv_callback_mode mode)
 {
-       c->batched_reading = state;
+       c->callback_mode = mode;
 }
 
 static inline void set_per_channel_state(struct vmbus_channel *c, void *s)