/*
  * vmbus_on_event - Process a channel event notification
+ *
+ * For batched channels (default) 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.
+ *    If this tasklet has been running for a long time
+ *    then reschedule ourselves.
  */
 void vmbus_on_event(unsigned long data)
 {
        struct vmbus_channel *channel = (void *) data;
-       void (*callback_fn)(void *);
+       unsigned long time_limit = jiffies + 2;
 
-       /*
-        * A channel once created is persistent even when there
-        * is no driver handling the device. An unloading driver
-        * sets the onchannel_callback to NULL on the same CPU
-        * as where this interrupt is handled (in an interrupt context).
-        * Thus, checking and invoking the driver specific callback takes
-        * care of orderly unloading of the driver.
-        */
-       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:
-                * 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 {
+               void (*callback_fn)(void *);
+
+               /* A channel once created is persistent even when
+                * there is no driver handling the device. An
+                * unloading driver sets the onchannel_callback to NULL.
                 */
-               if (hv_end_read(&channel->inbound) != 0) {
-                       hv_begin_read(&channel->inbound);
+               callback_fn = READ_ONCE(channel->onchannel_callback);
+               if (unlikely(callback_fn == NULL))
+                       return;
 
-                       tasklet_schedule(&channel->callback_event);
-               }
-       }
+               (*callback_fn)(channel->channel_callback_context);
+
+               if (channel->callback_mode != HV_CALL_BATCHED)
+                       return;
+
+               if (likely(hv_end_read(&channel->inbound) == 0))
+                       return;
+
+               hv_begin_read(&channel->inbound);
+       } while (likely(time_before(jiffies, time_limit)));
+
+       /* The time limit (2 jiffies) has been reached */
+       tasklet_schedule(&channel->callback_event);
 }
 
 /*