diff options
Diffstat (limited to 'drivers/hv/connection.c')
-rw-r--r-- | drivers/hv/connection.c | 65 |
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 | */ |
300 | void vmbus_on_event(unsigned long data) | 311 | void 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 | /* |