diff options
author | Suravee Suthikulpanit <suravee.suthikulpanit@amd.com> | 2016-08-23 14:52:42 -0400 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2016-09-08 07:11:56 -0400 |
commit | 5881f73757cc3dbada878e67c119a801ed0f9a07 (patch) | |
tree | 48fc4431d0f19972a5b9629b8a8293e8d62bda62 | |
parent | 5ea11f2b31b83bd3c05357e6bec20f598e00705a (diff) |
svm: Introduce AMD IOMMU avic_ga_log_notifier
This patch introduces avic_ga_log_notifier, which will be called
by IOMMU driver whenever it handles the Guest vAPIC (GA) log entry.
Reviewed-by: Radim Krčmář <rkrcmar@redhat.com>
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rw-r--r-- | arch/x86/include/asm/kvm_host.h | 1 | ||||
-rw-r--r-- | arch/x86/kvm/svm.c | 70 |
2 files changed, 69 insertions, 2 deletions
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index d36176bb1b6d..4c738c206be3 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h | |||
@@ -785,6 +785,7 @@ struct kvm_arch { | |||
785 | u32 ldr_mode; | 785 | u32 ldr_mode; |
786 | struct page *avic_logical_id_table_page; | 786 | struct page *avic_logical_id_table_page; |
787 | struct page *avic_physical_id_table_page; | 787 | struct page *avic_physical_id_table_page; |
788 | struct hlist_node hnode; | ||
788 | 789 | ||
789 | bool x2apic_format; | 790 | bool x2apic_format; |
790 | bool x2apic_broadcast_quirk_disabled; | 791 | bool x2apic_broadcast_quirk_disabled; |
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index 27e37d8044b3..0a9e43b2bd26 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c | |||
@@ -34,6 +34,8 @@ | |||
34 | #include <linux/sched.h> | 34 | #include <linux/sched.h> |
35 | #include <linux/trace_events.h> | 35 | #include <linux/trace_events.h> |
36 | #include <linux/slab.h> | 36 | #include <linux/slab.h> |
37 | #include <linux/amd-iommu.h> | ||
38 | #include <linux/hashtable.h> | ||
37 | 39 | ||
38 | #include <asm/apic.h> | 40 | #include <asm/apic.h> |
39 | #include <asm/perf_event.h> | 41 | #include <asm/perf_event.h> |
@@ -945,6 +947,55 @@ static void svm_disable_lbrv(struct vcpu_svm *svm) | |||
945 | set_msr_interception(msrpm, MSR_IA32_LASTINTTOIP, 0, 0); | 947 | set_msr_interception(msrpm, MSR_IA32_LASTINTTOIP, 0, 0); |
946 | } | 948 | } |
947 | 949 | ||
950 | /* Note: | ||
951 | * This hash table is used to map VM_ID to a struct kvm_arch, | ||
952 | * when handling AMD IOMMU GALOG notification to schedule in | ||
953 | * a particular vCPU. | ||
954 | */ | ||
955 | #define SVM_VM_DATA_HASH_BITS 8 | ||
956 | DECLARE_HASHTABLE(svm_vm_data_hash, SVM_VM_DATA_HASH_BITS); | ||
957 | static spinlock_t svm_vm_data_hash_lock; | ||
958 | |||
959 | /* Note: | ||
960 | * This function is called from IOMMU driver to notify | ||
961 | * SVM to schedule in a particular vCPU of a particular VM. | ||
962 | */ | ||
963 | static int avic_ga_log_notifier(u32 ga_tag) | ||
964 | { | ||
965 | unsigned long flags; | ||
966 | struct kvm_arch *ka = NULL; | ||
967 | struct kvm_vcpu *vcpu = NULL; | ||
968 | u32 vm_id = AVIC_GATAG_TO_VMID(ga_tag); | ||
969 | u32 vcpu_id = AVIC_GATAG_TO_VCPUID(ga_tag); | ||
970 | |||
971 | pr_debug("SVM: %s: vm_id=%#x, vcpu_id=%#x\n", __func__, vm_id, vcpu_id); | ||
972 | |||
973 | spin_lock_irqsave(&svm_vm_data_hash_lock, flags); | ||
974 | hash_for_each_possible(svm_vm_data_hash, ka, hnode, vm_id) { | ||
975 | struct kvm *kvm = container_of(ka, struct kvm, arch); | ||
976 | struct kvm_arch *vm_data = &kvm->arch; | ||
977 | |||
978 | if (vm_data->avic_vm_id != vm_id) | ||
979 | continue; | ||
980 | vcpu = kvm_get_vcpu_by_id(kvm, vcpu_id); | ||
981 | break; | ||
982 | } | ||
983 | spin_unlock_irqrestore(&svm_vm_data_hash_lock, flags); | ||
984 | |||
985 | if (!vcpu) | ||
986 | return 0; | ||
987 | |||
988 | /* Note: | ||
989 | * At this point, the IOMMU should have already set the pending | ||
990 | * bit in the vAPIC backing page. So, we just need to schedule | ||
991 | * in the vcpu. | ||
992 | */ | ||
993 | if (vcpu->mode == OUTSIDE_GUEST_MODE) | ||
994 | kvm_vcpu_wake_up(vcpu); | ||
995 | |||
996 | return 0; | ||
997 | } | ||
998 | |||
948 | static __init int svm_hardware_setup(void) | 999 | static __init int svm_hardware_setup(void) |
949 | { | 1000 | { |
950 | int cpu; | 1001 | int cpu; |
@@ -1003,10 +1054,15 @@ static __init int svm_hardware_setup(void) | |||
1003 | if (avic) { | 1054 | if (avic) { |
1004 | if (!npt_enabled || | 1055 | if (!npt_enabled || |
1005 | !boot_cpu_has(X86_FEATURE_AVIC) || | 1056 | !boot_cpu_has(X86_FEATURE_AVIC) || |
1006 | !IS_ENABLED(CONFIG_X86_LOCAL_APIC)) | 1057 | !IS_ENABLED(CONFIG_X86_LOCAL_APIC)) { |
1007 | avic = false; | 1058 | avic = false; |
1008 | else | 1059 | } else { |
1009 | pr_info("AVIC enabled\n"); | 1060 | pr_info("AVIC enabled\n"); |
1061 | |||
1062 | hash_init(svm_vm_data_hash); | ||
1063 | spin_lock_init(&svm_vm_data_hash_lock); | ||
1064 | amd_iommu_register_ga_log_notifier(&avic_ga_log_notifier); | ||
1065 | } | ||
1010 | } | 1066 | } |
1011 | 1067 | ||
1012 | return 0; | 1068 | return 0; |
@@ -1327,6 +1383,7 @@ static inline int avic_free_vm_id(int id) | |||
1327 | 1383 | ||
1328 | static void avic_vm_destroy(struct kvm *kvm) | 1384 | static void avic_vm_destroy(struct kvm *kvm) |
1329 | { | 1385 | { |
1386 | unsigned long flags; | ||
1330 | struct kvm_arch *vm_data = &kvm->arch; | 1387 | struct kvm_arch *vm_data = &kvm->arch; |
1331 | 1388 | ||
1332 | avic_free_vm_id(vm_data->avic_vm_id); | 1389 | avic_free_vm_id(vm_data->avic_vm_id); |
@@ -1335,10 +1392,15 @@ static void avic_vm_destroy(struct kvm *kvm) | |||
1335 | __free_page(vm_data->avic_logical_id_table_page); | 1392 | __free_page(vm_data->avic_logical_id_table_page); |
1336 | if (vm_data->avic_physical_id_table_page) | 1393 | if (vm_data->avic_physical_id_table_page) |
1337 | __free_page(vm_data->avic_physical_id_table_page); | 1394 | __free_page(vm_data->avic_physical_id_table_page); |
1395 | |||
1396 | spin_lock_irqsave(&svm_vm_data_hash_lock, flags); | ||
1397 | hash_del(&vm_data->hnode); | ||
1398 | spin_unlock_irqrestore(&svm_vm_data_hash_lock, flags); | ||
1338 | } | 1399 | } |
1339 | 1400 | ||
1340 | static int avic_vm_init(struct kvm *kvm) | 1401 | static int avic_vm_init(struct kvm *kvm) |
1341 | { | 1402 | { |
1403 | unsigned long flags; | ||
1342 | int err = -ENOMEM; | 1404 | int err = -ENOMEM; |
1343 | struct kvm_arch *vm_data = &kvm->arch; | 1405 | struct kvm_arch *vm_data = &kvm->arch; |
1344 | struct page *p_page; | 1406 | struct page *p_page; |
@@ -1367,6 +1429,10 @@ static int avic_vm_init(struct kvm *kvm) | |||
1367 | vm_data->avic_logical_id_table_page = l_page; | 1429 | vm_data->avic_logical_id_table_page = l_page; |
1368 | clear_page(page_address(l_page)); | 1430 | clear_page(page_address(l_page)); |
1369 | 1431 | ||
1432 | spin_lock_irqsave(&svm_vm_data_hash_lock, flags); | ||
1433 | hash_add(svm_vm_data_hash, &vm_data->hnode, vm_data->avic_vm_id); | ||
1434 | spin_unlock_irqrestore(&svm_vm_data_hash_lock, flags); | ||
1435 | |||
1370 | return 0; | 1436 | return 0; |
1371 | 1437 | ||
1372 | free_avic: | 1438 | free_avic: |