aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSuravee Suthikulpanit <suravee.suthikulpanit@amd.com>2016-08-23 14:52:42 -0400
committerPaolo Bonzini <pbonzini@redhat.com>2016-09-08 07:11:56 -0400
commit5881f73757cc3dbada878e67c119a801ed0f9a07 (patch)
tree48fc4431d0f19972a5b9629b8a8293e8d62bda62
parent5ea11f2b31b83bd3c05357e6bec20f598e00705a (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.h1
-rw-r--r--arch/x86/kvm/svm.c70
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
956DECLARE_HASHTABLE(svm_vm_data_hash, SVM_VM_DATA_HASH_BITS);
957static 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 */
963static 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
948static __init int svm_hardware_setup(void) 999static __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
1328static void avic_vm_destroy(struct kvm *kvm) 1384static 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
1340static int avic_vm_init(struct kvm *kvm) 1401static 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
1372free_avic: 1438free_avic: