Skip to content

Commit 9636be8

Browse files
kelleymhliuw
authored andcommitted
x86/hyperv: Fix hyperv_pcpu_input_arg handling when CPUs go online/offline
These commits a494aef ("PCI: hv: Replace retarget_msi_interrupt_params with hyperv_pcpu_input_arg") 2c6ba42 ("PCI: hv: Enable PCI pass-thru devices in Confidential VMs") update the Hyper-V virtual PCI driver to use the hyperv_pcpu_input_arg because that memory will be correctly marked as decrypted or encrypted for all VM types (CoCo or normal). But problems ensue when CPUs in the VM go online or offline after virtual PCI devices have been configured. When a CPU is brought online, the hyperv_pcpu_input_arg for that CPU is initialized by hv_cpu_init() running under state CPUHP_AP_ONLINE_DYN. But this state occurs after state CPUHP_AP_IRQ_AFFINITY_ONLINE, which may call the virtual PCI driver and fault trying to use the as yet uninitialized hyperv_pcpu_input_arg. A similar problem occurs in a CoCo VM if the MMIO read and write hypercalls are used from state CPUHP_AP_IRQ_AFFINITY_ONLINE. When a CPU is taken offline, IRQs may be reassigned in state CPUHP_TEARDOWN_CPU. Again, the virtual PCI driver may fault trying to use the hyperv_pcpu_input_arg that has already been freed by a higher state. Fix the onlining problem by adding state CPUHP_AP_HYPERV_ONLINE immediately after CPUHP_AP_ONLINE_IDLE (similar to CPUHP_AP_KVM_ONLINE) and before CPUHP_AP_IRQ_AFFINITY_ONLINE. Use this new state for Hyper-V initialization so that hyperv_pcpu_input_arg is allocated early enough. Fix the offlining problem by not freeing hyperv_pcpu_input_arg when a CPU goes offline. Retain the allocated memory, and reuse it if the CPU comes back online later. Signed-off-by: Michael Kelley <[email protected]> Reviewed-by: Vitaly Kuznetsov <[email protected]> Acked-by: Borislav Petkov (AMD) <[email protected]> Reviewed-by: Dexuan Cui <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Wei Liu <[email protected]>
1 parent 320805a commit 9636be8

File tree

3 files changed

+26
-25
lines changed

3 files changed

+26
-25
lines changed

arch/x86/hyperv/hv_init.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ void __init hyperv_init(void)
416416
goto free_vp_assist_page;
417417
}
418418

419-
cpuhp = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/hyperv_init:online",
419+
cpuhp = cpuhp_setup_state(CPUHP_AP_HYPERV_ONLINE, "x86/hyperv_init:online",
420420
hv_cpu_init, hv_cpu_die);
421421
if (cpuhp < 0)
422422
goto free_ghcb_page;

drivers/hv/hv_common.c

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -364,13 +364,20 @@ int hv_common_cpu_init(unsigned int cpu)
364364
flags = irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL;
365365

366366
inputarg = (void **)this_cpu_ptr(hyperv_pcpu_input_arg);
367-
*inputarg = kmalloc(pgcount * HV_HYP_PAGE_SIZE, flags);
368-
if (!(*inputarg))
369-
return -ENOMEM;
370367

371-
if (hv_root_partition) {
372-
outputarg = (void **)this_cpu_ptr(hyperv_pcpu_output_arg);
373-
*outputarg = (char *)(*inputarg) + HV_HYP_PAGE_SIZE;
368+
/*
369+
* hyperv_pcpu_input_arg and hyperv_pcpu_output_arg memory is already
370+
* allocated if this CPU was previously online and then taken offline
371+
*/
372+
if (!*inputarg) {
373+
*inputarg = kmalloc(pgcount * HV_HYP_PAGE_SIZE, flags);
374+
if (!(*inputarg))
375+
return -ENOMEM;
376+
377+
if (hv_root_partition) {
378+
outputarg = (void **)this_cpu_ptr(hyperv_pcpu_output_arg);
379+
*outputarg = (char *)(*inputarg) + HV_HYP_PAGE_SIZE;
380+
}
374381
}
375382

376383
msr_vp_index = hv_get_register(HV_REGISTER_VP_INDEX);
@@ -385,24 +392,17 @@ int hv_common_cpu_init(unsigned int cpu)
385392

386393
int hv_common_cpu_die(unsigned int cpu)
387394
{
388-
unsigned long flags;
389-
void **inputarg, **outputarg;
390-
void *mem;
391-
392-
local_irq_save(flags);
393-
394-
inputarg = (void **)this_cpu_ptr(hyperv_pcpu_input_arg);
395-
mem = *inputarg;
396-
*inputarg = NULL;
397-
398-
if (hv_root_partition) {
399-
outputarg = (void **)this_cpu_ptr(hyperv_pcpu_output_arg);
400-
*outputarg = NULL;
401-
}
402-
403-
local_irq_restore(flags);
404-
405-
kfree(mem);
395+
/*
396+
* The hyperv_pcpu_input_arg and hyperv_pcpu_output_arg memory
397+
* is not freed when the CPU goes offline as the hyperv_pcpu_input_arg
398+
* may be used by the Hyper-V vPCI driver in reassigning interrupts
399+
* as part of the offlining process. The interrupt reassignment
400+
* happens *after* the CPUHP_AP_HYPERV_ONLINE state has run and
401+
* called this function.
402+
*
403+
* If a previously offlined CPU is brought back online again, the
404+
* originally allocated memory is reused in hv_common_cpu_init().
405+
*/
406406

407407
return 0;
408408
}

include/linux/cpuhotplug.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ enum cpuhp_state {
200200

201201
/* Online section invoked on the hotplugged CPU from the hotplug thread */
202202
CPUHP_AP_ONLINE_IDLE,
203+
CPUHP_AP_HYPERV_ONLINE,
203204
CPUHP_AP_KVM_ONLINE,
204205
CPUHP_AP_SCHED_WAIT_EMPTY,
205206
CPUHP_AP_SMPBOOT_THREADS,

0 commit comments

Comments
 (0)