diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index a8366fec14581f795c046dcd47baef7a56f20692..fce27fb141cc618d99099f569d1d5a63ac7f53f9 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -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); } /*