summaryrefslogtreecommitdiffstats
path: root/drivers/hv/connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hv/connection.c')
-rw-r--r--drivers/hv/connection.c65
1 files changed, 34 insertions, 31 deletions
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index a8366fec1458..fce27fb141cc 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -296,44 +296,47 @@ struct vmbus_channel *relid2channel(u32 relid)
296 296
297/* 297/*
298 * vmbus_on_event - Process a channel event notification 298 * vmbus_on_event - Process a channel event notification
299 *
300 * For batched channels (default) optimize host to guest signaling
301 * by ensuring:
302 * 1. While reading the channel, we disable interrupts from host.
303 * 2. Ensure that we process all posted messages from the host
304 * before returning from this callback.
305 * 3. Once we return, enable signaling from the host. Once this
306 * state is set we check to see if additional packets are
307 * available to read. In this case we repeat the process.
308 * If this tasklet has been running for a long time
309 * then reschedule ourselves.
299 */ 310 */
300void vmbus_on_event(unsigned long data) 311void vmbus_on_event(unsigned long data)
301{ 312{
302 struct vmbus_channel *channel = (void *) data; 313 struct vmbus_channel *channel = (void *) data;
303 void (*callback_fn)(void *); 314 unsigned long time_limit = jiffies + 2;
304 315
305 /* 316 do {
306 * A channel once created is persistent even when there 317 void (*callback_fn)(void *);
307 * is no driver handling the device. An unloading driver 318
308 * sets the onchannel_callback to NULL on the same CPU 319 /* A channel once created is persistent even when
309 * as where this interrupt is handled (in an interrupt context). 320 * there is no driver handling the device. An
310 * Thus, checking and invoking the driver specific callback takes 321 * unloading driver sets the onchannel_callback to NULL.
311 * care of orderly unloading of the driver.
312 */
313 callback_fn = READ_ONCE(channel->onchannel_callback);
314 if (unlikely(callback_fn == NULL))
315 return;
316
317 (*callback_fn)(channel->channel_callback_context);
318
319 if (channel->callback_mode == HV_CALL_BATCHED) {
320 /*
321 * This callback reads the messages sent by the host.
322 * We can optimize host to guest signaling by ensuring:
323 * 1. While reading the channel, we disable interrupts from
324 * host.
325 * 2. Ensure that we process all posted messages from the host
326 * before returning from this callback.
327 * 3. Once we return, enable signaling from the host. Once this
328 * state is set we check to see if additional packets are
329 * available to read. In this case we repeat the process.
330 */ 322 */
331 if (hv_end_read(&channel->inbound) != 0) { 323 callback_fn = READ_ONCE(channel->onchannel_callback);
332 hv_begin_read(&channel->inbound); 324 if (unlikely(callback_fn == NULL))
325 return;
333 326
334 tasklet_schedule(&channel->callback_event); 327 (*callback_fn)(channel->channel_callback_context);
335 } 328
336 } 329 if (channel->callback_mode != HV_CALL_BATCHED)
330 return;
331
332 if (likely(hv_end_read(&channel->inbound) == 0))
333 return;
334
335 hv_begin_read(&channel->inbound);
336 } while (likely(time_before(jiffies, time_limit)));
337
338 /* The time limit (2 jiffies) has been reached */
339 tasklet_schedule(&channel->callback_event);
337} 340}
338 341
339/* 342/*