diff options
Diffstat (limited to 'drivers/hv/channel.c')
-rw-r--r-- | drivers/hv/channel.c | 27 |
1 files changed, 13 insertions, 14 deletions
diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 81a80c82f1bd..321b8833fa6f 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c | |||
@@ -502,12 +502,15 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) | |||
502 | 502 | ||
503 | wait_for_completion(&info->waitevent); | 503 | wait_for_completion(&info->waitevent); |
504 | 504 | ||
505 | if (channel->rescind) { | ||
506 | ret = -ENODEV; | ||
507 | goto post_msg_err; | ||
508 | } | ||
509 | |||
510 | post_msg_err: | 505 | post_msg_err: |
506 | /* | ||
507 | * If the channel has been rescinded; | ||
508 | * we will be awakened by the rescind | ||
509 | * handler; set the error code to zero so we don't leak memory. | ||
510 | */ | ||
511 | if (channel->rescind) | ||
512 | ret = 0; | ||
513 | |||
511 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | 514 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); |
512 | list_del(&info->msglistentry); | 515 | list_del(&info->msglistentry); |
513 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | 516 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); |
@@ -530,20 +533,18 @@ static int vmbus_close_internal(struct vmbus_channel *channel) | |||
530 | int ret; | 533 | int ret; |
531 | 534 | ||
532 | /* | 535 | /* |
533 | * vmbus_on_event(), running in the tasklet, can race | 536 | * vmbus_on_event(), running in the per-channel tasklet, can race |
534 | * with vmbus_close_internal() in the case of SMP guest, e.g., when | 537 | * with vmbus_close_internal() in the case of SMP guest, e.g., when |
535 | * the former is accessing channel->inbound.ring_buffer, the latter | 538 | * the former is accessing channel->inbound.ring_buffer, the latter |
536 | * could be freeing the ring_buffer pages. | 539 | * could be freeing the ring_buffer pages, so here we must stop it |
537 | * | 540 | * first. |
538 | * To resolve the race, we can serialize them by disabling the | ||
539 | * tasklet when the latter is running here. | ||
540 | */ | 541 | */ |
541 | hv_event_tasklet_disable(channel); | 542 | tasklet_disable(&channel->callback_event); |
542 | 543 | ||
543 | /* | 544 | /* |
544 | * In case a device driver's probe() fails (e.g., | 545 | * In case a device driver's probe() fails (e.g., |
545 | * util_probe() -> vmbus_open() returns -ENOMEM) and the device is | 546 | * util_probe() -> vmbus_open() returns -ENOMEM) and the device is |
546 | * rescinded later (e.g., we dynamically disble an Integrated Service | 547 | * rescinded later (e.g., we dynamically disable an Integrated Service |
547 | * in Hyper-V Manager), the driver's remove() invokes vmbus_close(): | 548 | * in Hyper-V Manager), the driver's remove() invokes vmbus_close(): |
548 | * here we should skip most of the below cleanup work. | 549 | * here we should skip most of the below cleanup work. |
549 | */ | 550 | */ |
@@ -605,8 +606,6 @@ static int vmbus_close_internal(struct vmbus_channel *channel) | |||
605 | get_order(channel->ringbuffer_pagecount * PAGE_SIZE)); | 606 | get_order(channel->ringbuffer_pagecount * PAGE_SIZE)); |
606 | 607 | ||
607 | out: | 608 | out: |
608 | hv_event_tasklet_enable(channel); | ||
609 | |||
610 | return ret; | 609 | return ret; |
611 | } | 610 | } |
612 | 611 | ||