Skip to content

Commit d3b26dd

Browse files
dcuigregkh
authored andcommitted
Drivers: hv: vmbus: Reset the channel callback in vmbus_onoffer_rescind()
Before setting channel->rescind in vmbus_rescind_cleanup(), we should make sure the channel callback won't run any more, otherwise a high-level driver like pci_hyperv, which may be infinitely waiting for the host VSP's response and notices the channel has been rescinded, can't safely give up: e.g., in hv_pci_protocol_negotiation() -> wait_for_response(), it's unsafe to exit from wait_for_response() and proceed with the on-stack variable "comp_pkt" popped. The issue was originally spotted by Michael Kelley <[email protected]>. In vmbus_close_internal(), the patch also minimizes the range protected by disabling/enabling channel->callback_event: we don't really need that for the whole function. Signed-off-by: Dexuan Cui <[email protected]> Reviewed-by: Michael Kelley <[email protected]> Cc: [email protected] Cc: K. Y. Srinivasan <[email protected]> Cc: Stephen Hemminger <[email protected]> Cc: Michael Kelley <[email protected]> Signed-off-by: K. Y. Srinivasan <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 7026a5f commit d3b26dd

File tree

3 files changed

+32
-16
lines changed

3 files changed

+32
-16
lines changed

drivers/hv/channel.c

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -558,11 +558,8 @@ static void reset_channel_cb(void *arg)
558558
channel->onchannel_callback = NULL;
559559
}
560560

561-
static int vmbus_close_internal(struct vmbus_channel *channel)
561+
void vmbus_reset_channel_cb(struct vmbus_channel *channel)
562562
{
563-
struct vmbus_channel_close_channel *msg;
564-
int ret;
565-
566563
/*
567564
* vmbus_on_event(), running in the per-channel tasklet, can race
568565
* with vmbus_close_internal() in the case of SMP guest, e.g., when
@@ -572,6 +569,29 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
572569
*/
573570
tasklet_disable(&channel->callback_event);
574571

572+
channel->sc_creation_callback = NULL;
573+
574+
/* Stop the callback asap */
575+
if (channel->target_cpu != get_cpu()) {
576+
put_cpu();
577+
smp_call_function_single(channel->target_cpu, reset_channel_cb,
578+
channel, true);
579+
} else {
580+
reset_channel_cb(channel);
581+
put_cpu();
582+
}
583+
584+
/* Re-enable tasklet for use on re-open */
585+
tasklet_enable(&channel->callback_event);
586+
}
587+
588+
static int vmbus_close_internal(struct vmbus_channel *channel)
589+
{
590+
struct vmbus_channel_close_channel *msg;
591+
int ret;
592+
593+
vmbus_reset_channel_cb(channel);
594+
575595
/*
576596
* In case a device driver's probe() fails (e.g.,
577597
* util_probe() -> vmbus_open() returns -ENOMEM) and the device is
@@ -585,16 +605,6 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
585605
}
586606

587607
channel->state = CHANNEL_OPEN_STATE;
588-
channel->sc_creation_callback = NULL;
589-
/* Stop callback and cancel the timer asap */
590-
if (channel->target_cpu != get_cpu()) {
591-
put_cpu();
592-
smp_call_function_single(channel->target_cpu, reset_channel_cb,
593-
channel, true);
594-
} else {
595-
reset_channel_cb(channel);
596-
put_cpu();
597-
}
598608

599609
/* Send a closing message */
600610

@@ -639,8 +649,6 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
639649
get_order(channel->ringbuffer_pagecount * PAGE_SIZE));
640650

641651
out:
642-
/* re-enable tasklet for use on re-open */
643-
tasklet_enable(&channel->callback_event);
644652
return ret;
645653
}
646654

drivers/hv/channel_mgmt.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,12 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
892892
return;
893893
}
894894

895+
/*
896+
* Before setting channel->rescind in vmbus_rescind_cleanup(), we
897+
* should make sure the channel callback is not running any more.
898+
*/
899+
vmbus_reset_channel_cb(channel);
900+
895901
/*
896902
* Now wait for offer handling to complete.
897903
*/

include/linux/hyperv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,8 @@ extern int vmbus_establish_gpadl(struct vmbus_channel *channel,
10611061
extern int vmbus_teardown_gpadl(struct vmbus_channel *channel,
10621062
u32 gpadl_handle);
10631063

1064+
void vmbus_reset_channel_cb(struct vmbus_channel *channel);
1065+
10641066
extern int vmbus_recvpacket(struct vmbus_channel *channel,
10651067
void *buffer,
10661068
u32 bufferlen,

0 commit comments

Comments
 (0)