diff options
author | Wanpeng Li <wanpengli@tencent.com> | 2018-07-23 02:39:52 -0400 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2018-08-06 11:59:22 -0400 |
commit | aaffcfd1e82d3378538408d0310b7424b98d8f81 (patch) | |
tree | 22101f976e8eac588ce0ae1fade89df4c50204ab | |
parent | d63bae079b6426afc998c5ea76d9cde8d8c98303 (diff) |
KVM: X86: Implement PV IPIs in linux guest
Implement paravirtual apic hooks to enable PV IPIs for KVM if the "send IPI"
hypercall is available. The hypercall lets a guest send IPIs, with
at most 128 destinations per hypercall in 64-bit mode and 64 vCPUs per
hypercall in 32-bit mode.
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Radim Krčmář <rkrcmar@redhat.com>
Cc: Vitaly Kuznetsov <vkuznets@redhat.com>
Signed-off-by: Wanpeng Li <wanpengli@tencent.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rw-r--r-- | arch/x86/include/uapi/asm/kvm_para.h | 1 | ||||
-rw-r--r-- | arch/x86/kernel/kvm.c | 96 | ||||
-rw-r--r-- | include/uapi/linux/kvm_para.h | 1 |
3 files changed, 98 insertions, 0 deletions
diff --git a/arch/x86/include/uapi/asm/kvm_para.h b/arch/x86/include/uapi/asm/kvm_para.h index 0ede697c3961..19980ec1a316 100644 --- a/arch/x86/include/uapi/asm/kvm_para.h +++ b/arch/x86/include/uapi/asm/kvm_para.h | |||
@@ -28,6 +28,7 @@ | |||
28 | #define KVM_FEATURE_PV_UNHALT 7 | 28 | #define KVM_FEATURE_PV_UNHALT 7 |
29 | #define KVM_FEATURE_PV_TLB_FLUSH 9 | 29 | #define KVM_FEATURE_PV_TLB_FLUSH 9 |
30 | #define KVM_FEATURE_ASYNC_PF_VMEXIT 10 | 30 | #define KVM_FEATURE_ASYNC_PF_VMEXIT 10 |
31 | #define KVM_FEATURE_PV_SEND_IPI 11 | ||
31 | 32 | ||
32 | #define KVM_HINTS_REALTIME 0 | 33 | #define KVM_HINTS_REALTIME 0 |
33 | 34 | ||
diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c index 39d79720380f..62cbd089f709 100644 --- a/arch/x86/kernel/kvm.c +++ b/arch/x86/kernel/kvm.c | |||
@@ -454,6 +454,98 @@ static void __init sev_map_percpu_data(void) | |||
454 | } | 454 | } |
455 | 455 | ||
456 | #ifdef CONFIG_SMP | 456 | #ifdef CONFIG_SMP |
457 | #define KVM_IPI_CLUSTER_SIZE (2 * BITS_PER_LONG) | ||
458 | |||
459 | static void __send_ipi_mask(const struct cpumask *mask, int vector) | ||
460 | { | ||
461 | unsigned long flags; | ||
462 | int cpu, apic_id, icr; | ||
463 | int min = 0, max = 0; | ||
464 | #ifdef CONFIG_X86_64 | ||
465 | __uint128_t ipi_bitmap = 0; | ||
466 | #else | ||
467 | u64 ipi_bitmap = 0; | ||
468 | #endif | ||
469 | |||
470 | if (cpumask_empty(mask)) | ||
471 | return; | ||
472 | |||
473 | local_irq_save(flags); | ||
474 | |||
475 | switch (vector) { | ||
476 | default: | ||
477 | icr = APIC_DM_FIXED | vector; | ||
478 | break; | ||
479 | case NMI_VECTOR: | ||
480 | icr = APIC_DM_NMI; | ||
481 | break; | ||
482 | } | ||
483 | |||
484 | for_each_cpu(cpu, mask) { | ||
485 | apic_id = per_cpu(x86_cpu_to_apicid, cpu); | ||
486 | if (!ipi_bitmap) { | ||
487 | min = max = apic_id; | ||
488 | } else if (apic_id < min && max - apic_id < KVM_IPI_CLUSTER_SIZE) { | ||
489 | ipi_bitmap <<= min - apic_id; | ||
490 | min = apic_id; | ||
491 | } else if (apic_id < min + KVM_IPI_CLUSTER_SIZE) { | ||
492 | max = apic_id < max ? max : apic_id; | ||
493 | } else { | ||
494 | kvm_hypercall4(KVM_HC_SEND_IPI, (unsigned long)ipi_bitmap, | ||
495 | (unsigned long)(ipi_bitmap >> BITS_PER_LONG), min, icr); | ||
496 | min = max = apic_id; | ||
497 | ipi_bitmap = 0; | ||
498 | } | ||
499 | __set_bit(apic_id - min, (unsigned long *)&ipi_bitmap); | ||
500 | } | ||
501 | |||
502 | if (ipi_bitmap) { | ||
503 | kvm_hypercall4(KVM_HC_SEND_IPI, (unsigned long)ipi_bitmap, | ||
504 | (unsigned long)(ipi_bitmap >> BITS_PER_LONG), min, icr); | ||
505 | } | ||
506 | |||
507 | local_irq_restore(flags); | ||
508 | } | ||
509 | |||
510 | static void kvm_send_ipi_mask(const struct cpumask *mask, int vector) | ||
511 | { | ||
512 | __send_ipi_mask(mask, vector); | ||
513 | } | ||
514 | |||
515 | static void kvm_send_ipi_mask_allbutself(const struct cpumask *mask, int vector) | ||
516 | { | ||
517 | unsigned int this_cpu = smp_processor_id(); | ||
518 | struct cpumask new_mask; | ||
519 | const struct cpumask *local_mask; | ||
520 | |||
521 | cpumask_copy(&new_mask, mask); | ||
522 | cpumask_clear_cpu(this_cpu, &new_mask); | ||
523 | local_mask = &new_mask; | ||
524 | __send_ipi_mask(local_mask, vector); | ||
525 | } | ||
526 | |||
527 | static void kvm_send_ipi_allbutself(int vector) | ||
528 | { | ||
529 | kvm_send_ipi_mask_allbutself(cpu_online_mask, vector); | ||
530 | } | ||
531 | |||
532 | static void kvm_send_ipi_all(int vector) | ||
533 | { | ||
534 | __send_ipi_mask(cpu_online_mask, vector); | ||
535 | } | ||
536 | |||
537 | /* | ||
538 | * Set the IPI entry points | ||
539 | */ | ||
540 | static void kvm_setup_pv_ipi(void) | ||
541 | { | ||
542 | apic->send_IPI_mask = kvm_send_ipi_mask; | ||
543 | apic->send_IPI_mask_allbutself = kvm_send_ipi_mask_allbutself; | ||
544 | apic->send_IPI_allbutself = kvm_send_ipi_allbutself; | ||
545 | apic->send_IPI_all = kvm_send_ipi_all; | ||
546 | pr_info("KVM setup pv IPIs\n"); | ||
547 | } | ||
548 | |||
457 | static void __init kvm_smp_prepare_cpus(unsigned int max_cpus) | 549 | static void __init kvm_smp_prepare_cpus(unsigned int max_cpus) |
458 | { | 550 | { |
459 | native_smp_prepare_cpus(max_cpus); | 551 | native_smp_prepare_cpus(max_cpus); |
@@ -626,6 +718,10 @@ static uint32_t __init kvm_detect(void) | |||
626 | 718 | ||
627 | static void __init kvm_apic_init(void) | 719 | static void __init kvm_apic_init(void) |
628 | { | 720 | { |
721 | #if defined(CONFIG_SMP) | ||
722 | if (kvm_para_has_feature(KVM_FEATURE_PV_SEND_IPI)) | ||
723 | kvm_setup_pv_ipi(); | ||
724 | #endif | ||
629 | } | 725 | } |
630 | 726 | ||
631 | static void __init kvm_init_platform(void) | 727 | static void __init kvm_init_platform(void) |
diff --git a/include/uapi/linux/kvm_para.h b/include/uapi/linux/kvm_para.h index f3893ef82b65..6c0ce49931e5 100644 --- a/include/uapi/linux/kvm_para.h +++ b/include/uapi/linux/kvm_para.h | |||
@@ -27,6 +27,7 @@ | |||
27 | #define KVM_HC_MIPS_EXIT_VM 7 | 27 | #define KVM_HC_MIPS_EXIT_VM 7 |
28 | #define KVM_HC_MIPS_CONSOLE_OUTPUT 8 | 28 | #define KVM_HC_MIPS_CONSOLE_OUTPUT 8 |
29 | #define KVM_HC_CLOCK_PAIRING 9 | 29 | #define KVM_HC_CLOCK_PAIRING 9 |
30 | #define KVM_HC_SEND_IPI 10 | ||
30 | 31 | ||
31 | /* | 32 | /* |
32 | * hypercalls use architecture specific | 33 | * hypercalls use architecture specific |