Skip to content

Commit 8a857c5

Browse files
andrea-parriliuw
authored andcommitted
Drivers: hv: vmbus: Always handle the VMBus messages on CPU0
A Linux guest have to pick a "connect CPU" to communicate with the Hyper-V host. This CPU can not be taken offline because Hyper-V does not provide a way to change that CPU assignment. Current code sets the connect CPU to whatever CPU ends up running the function vmbus_negotiate_version(), and this will generate problems if that CPU is taken offine. Establish CPU0 as the connect CPU, and add logics to prevents the connect CPU from being taken offline. We could pick some other CPU, and we could pick that "other CPU" dynamically if there was a reason to do so at some point in the future. But for now, #defining the connect CPU to 0 is the most straightforward and least complex solution. While on this, add inline comments explaining "why" offer and rescind messages should not be handled by a same serialized work queue. Suggested-by: Dexuan Cui <decui@microsoft.com> Signed-off-by: Andrea Parri (Microsoft) <parri.andrea@gmail.com> Reviewed-by: Vitaly Kuznetsov <vkuznets@redhat.com> Link: https://lore.kernel.org/r/20200406001514.19876-2-parri.andrea@gmail.com Reviewed-by: Michael Kelley <mikelley@microsoft.com> Signed-off-by: Wei Liu <wei.liu@kernel.org>
1 parent 52c7803 commit 8a857c5

File tree

4 files changed

+31
-27
lines changed

4 files changed

+31
-27
lines changed

drivers/hv/connection.c

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ MODULE_PARM_DESC(max_version,
6969
int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
7070
{
7171
int ret = 0;
72-
unsigned int cur_cpu;
7372
struct vmbus_channel_initiate_contact *msg;
7473
unsigned long flags;
7574

@@ -102,24 +101,7 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
102101

103102
msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages[0]);
104103
msg->monitor_page2 = virt_to_phys(vmbus_connection.monitor_pages[1]);
105-
/*
106-
* We want all channel messages to be delivered on CPU 0.
107-
* This has been the behavior pre-win8. This is not
108-
* perf issue and having all channel messages delivered on CPU 0
109-
* would be ok.
110-
* For post win8 hosts, we support receiving channel messagges on
111-
* all the CPUs. This is needed for kexec to work correctly where
112-
* the CPU attempting to connect may not be CPU 0.
113-
*/
114-
if (version >= VERSION_WIN8_1) {
115-
cur_cpu = get_cpu();
116-
msg->target_vcpu = hv_cpu_number_to_vp_number(cur_cpu);
117-
vmbus_connection.connect_cpu = cur_cpu;
118-
put_cpu();
119-
} else {
120-
msg->target_vcpu = 0;
121-
vmbus_connection.connect_cpu = 0;
122-
}
104+
msg->target_vcpu = hv_cpu_number_to_vp_number(VMBUS_CONNECT_CPU);
123105

124106
/*
125107
* Add to list before we send the request since we may

drivers/hv/hv.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,13 @@ int hv_synic_cleanup(unsigned int cpu)
249249
bool channel_found = false;
250250
unsigned long flags;
251251

252+
/*
253+
* Hyper-V does not provide a way to change the connect CPU once
254+
* it is set; we must prevent the connect CPU from going offline.
255+
*/
256+
if (cpu == VMBUS_CONNECT_CPU)
257+
return -EBUSY;
258+
252259
/*
253260
* Search for channels which are bound to the CPU we're about to
254261
* cleanup. In case we find one and vmbus is still connected we need to

drivers/hv/hyperv_vmbus.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,12 +212,13 @@ enum vmbus_connect_state {
212212

213213
#define MAX_SIZE_CHANNEL_MESSAGE HV_MESSAGE_PAYLOAD_BYTE_COUNT
214214

215-
struct vmbus_connection {
216-
/*
217-
* CPU on which the initial host contact was made.
218-
*/
219-
int connect_cpu;
215+
/*
216+
* The CPU that Hyper-V will interrupt for VMBUS messages, such as
217+
* CHANNELMSG_OFFERCHANNEL and CHANNELMSG_RESCIND_CHANNELOFFER.
218+
*/
219+
#define VMBUS_CONNECT_CPU 0
220220

221+
struct vmbus_connection {
221222
u32 msg_conn_id;
222223

223224
atomic_t offer_in_progress;

drivers/hv/vmbus_drv.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,14 +1109,28 @@ void vmbus_on_msg_dpc(unsigned long data)
11091109
/*
11101110
* If we are handling the rescind message;
11111111
* schedule the work on the global work queue.
1112+
*
1113+
* The OFFER message and the RESCIND message should
1114+
* not be handled by the same serialized work queue,
1115+
* because the OFFER handler may call vmbus_open(),
1116+
* which tries to open the channel by sending an
1117+
* OPEN_CHANNEL message to the host and waits for
1118+
* the host's response; however, if the host has
1119+
* rescinded the channel before it receives the
1120+
* OPEN_CHANNEL message, the host just silently
1121+
* ignores the OPEN_CHANNEL message; as a result,
1122+
* the guest's OFFER handler hangs for ever, if we
1123+
* handle the RESCIND message in the same serialized
1124+
* work queue: the RESCIND handler can not start to
1125+
* run before the OFFER handler finishes.
11121126
*/
1113-
schedule_work_on(vmbus_connection.connect_cpu,
1127+
schedule_work_on(VMBUS_CONNECT_CPU,
11141128
&ctx->work);
11151129
break;
11161130

11171131
case CHANNELMSG_OFFERCHANNEL:
11181132
atomic_inc(&vmbus_connection.offer_in_progress);
1119-
queue_work_on(vmbus_connection.connect_cpu,
1133+
queue_work_on(VMBUS_CONNECT_CPU,
11201134
vmbus_connection.work_queue,
11211135
&ctx->work);
11221136
break;
@@ -1164,7 +1178,7 @@ static void vmbus_force_channel_rescinded(struct vmbus_channel *channel)
11641178

11651179
INIT_WORK(&ctx->work, vmbus_onmessage_work);
11661180

1167-
queue_work_on(vmbus_connection.connect_cpu,
1181+
queue_work_on(VMBUS_CONNECT_CPU,
11681182
vmbus_connection.work_queue,
11691183
&ctx->work);
11701184
}

0 commit comments

Comments
 (0)