|
@@ -296,44 +296,47 @@ struct vmbus_channel *relid2channel(u32 relid)
|
|
|
|
|
|
/*
|
|
|
* 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);
|
|
|
}
|
|
|
|
|
|
/*
|