LoongArch: KVM: Add vcpu mapping from physical cpuid
authorBibo Mao <maobibo@loongson.cn>
Mon, 6 May 2024 14:00:47 +0000 (22:00 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Mon, 6 May 2024 14:00:47 +0000 (22:00 +0800)
Physical CPUID is used for interrupt routing for irqchips such as ipi,
msgint and eiointc interrupt controllers. Physical CPUID is stored at
the CSR register LOONGARCH_CSR_CPUID, it can not be changed once vcpu
is created and the physical CPUIDs of two vcpus cannot be the same.

Different irqchips have different size declaration about physical CPUID,
the max CPUID value for CSR LOONGARCH_CSR_CPUID on Loongson-3A5000 is
512, the max CPUID supported by IPI hardware is 1024, while for eiointc
irqchip is 256, and for msgint irqchip is 65536.

The smallest value from all interrupt controllers is selected now, and
the max cpuid size is defines as 256 by KVM which comes from the eiointc
irqchip.

Signed-off-by: Bibo Mao <maobibo@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
arch/loongarch/include/asm/kvm_host.h
arch/loongarch/include/asm/kvm_vcpu.h
arch/loongarch/kvm/vcpu.c
arch/loongarch/kvm/vm.c

index 2d62f7b0d377b5d81fb5f2ec292d7f990a728b44..897be17f686675580c1a41ab0774469182692bed 100644 (file)
@@ -64,6 +64,31 @@ struct kvm_world_switch {
 
 #define MAX_PGTABLE_LEVELS     4
 
+/*
+ * Physical CPUID is used for interrupt routing, there are different
+ * definitions about physical cpuid on different hardwares.
+ *
+ *  For LOONGARCH_CSR_CPUID register, max CPUID size if 512
+ *  For IPI hardware, max destination CPUID size 1024
+ *  For extioi interrupt controller, max destination CPUID size is 256
+ *  For msgint interrupt controller, max supported CPUID size is 65536
+ *
+ * Currently max CPUID is defined as 256 for KVM hypervisor, in future
+ * it will be expanded to 4096, including 16 packages at most. And every
+ * package supports at most 256 vcpus
+ */
+#define KVM_MAX_PHYID          256
+
+struct kvm_phyid_info {
+       struct kvm_vcpu *vcpu;
+       bool            enabled;
+};
+
+struct kvm_phyid_map {
+       int max_phyid;
+       struct kvm_phyid_info phys_map[KVM_MAX_PHYID];
+};
+
 struct kvm_arch {
        /* Guest physical mm */
        kvm_pte_t *pgd;
@@ -71,6 +96,8 @@ struct kvm_arch {
        unsigned long invalid_ptes[MAX_PGTABLE_LEVELS];
        unsigned int  pte_shifts[MAX_PGTABLE_LEVELS];
        unsigned int  root_level;
+       spinlock_t    phyid_map_lock;
+       struct kvm_phyid_map  *phyid_map;
 
        s64 time_offset;
        struct kvm_context __percpu *vmcs;
index 0cb4fdb8a9b5970dfefb24a34c82d1451ff27fa9..9f53950959daff43837f8c4d306e066e1af312cc 100644 (file)
@@ -81,6 +81,7 @@ void kvm_save_timer(struct kvm_vcpu *vcpu);
 void kvm_restore_timer(struct kvm_vcpu *vcpu);
 
 int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq);
+struct kvm_vcpu *kvm_get_vcpu_by_cpuid(struct kvm *kvm, int cpuid);
 
 /*
  * Loongarch KVM guest interrupt handling
index 3a8779065f73b45425f69b2cf204545dd2e0c908..0a32f1909631ed36ce3311c86fe9584a71f482ab 100644 (file)
@@ -250,6 +250,92 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
        return -EINVAL;
 }
 
+static inline int kvm_set_cpuid(struct kvm_vcpu *vcpu, u64 val)
+{
+       int cpuid;
+       struct kvm_phyid_map *map;
+       struct loongarch_csrs *csr = vcpu->arch.csr;
+
+       if (val >= KVM_MAX_PHYID)
+               return -EINVAL;
+
+       map = vcpu->kvm->arch.phyid_map;
+       cpuid = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_CPUID);
+
+       spin_lock(&vcpu->kvm->arch.phyid_map_lock);
+       if ((cpuid < KVM_MAX_PHYID) && map->phys_map[cpuid].enabled) {
+               /* Discard duplicated CPUID set operation */
+               if (cpuid == val) {
+                       spin_unlock(&vcpu->kvm->arch.phyid_map_lock);
+                       return 0;
+               }
+
+               /*
+                * CPUID is already set before
+                * Forbid changing to a different CPUID at runtime
+                */
+               spin_unlock(&vcpu->kvm->arch.phyid_map_lock);
+               return -EINVAL;
+       }
+
+       if (map->phys_map[val].enabled) {
+               /* Discard duplicated CPUID set operation */
+               if (vcpu == map->phys_map[val].vcpu) {
+                       spin_unlock(&vcpu->kvm->arch.phyid_map_lock);
+                       return 0;
+               }
+
+               /*
+                * New CPUID is already set with other vcpu
+                * Forbid sharing the same CPUID between different vcpus
+                */
+               spin_unlock(&vcpu->kvm->arch.phyid_map_lock);
+               return -EINVAL;
+       }
+
+       kvm_write_sw_gcsr(csr, LOONGARCH_CSR_CPUID, val);
+       map->phys_map[val].enabled      = true;
+       map->phys_map[val].vcpu         = vcpu;
+       spin_unlock(&vcpu->kvm->arch.phyid_map_lock);
+
+       return 0;
+}
+
+static inline void kvm_drop_cpuid(struct kvm_vcpu *vcpu)
+{
+       int cpuid;
+       struct kvm_phyid_map *map;
+       struct loongarch_csrs *csr = vcpu->arch.csr;
+
+       map = vcpu->kvm->arch.phyid_map;
+       cpuid = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_CPUID);
+
+       if (cpuid >= KVM_MAX_PHYID)
+               return;
+
+       spin_lock(&vcpu->kvm->arch.phyid_map_lock);
+       if (map->phys_map[cpuid].enabled) {
+               map->phys_map[cpuid].vcpu = NULL;
+               map->phys_map[cpuid].enabled = false;
+               kvm_write_sw_gcsr(csr, LOONGARCH_CSR_CPUID, KVM_MAX_PHYID);
+       }
+       spin_unlock(&vcpu->kvm->arch.phyid_map_lock);
+}
+
+struct kvm_vcpu *kvm_get_vcpu_by_cpuid(struct kvm *kvm, int cpuid)
+{
+       struct kvm_phyid_map *map;
+
+       if (cpuid >= KVM_MAX_PHYID)
+               return NULL;
+
+       map = kvm->arch.phyid_map;
+       if (!map->phys_map[cpuid].enabled)
+               return NULL;
+
+       return map->phys_map[cpuid].vcpu;
+}
+
 static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val)
 {
        unsigned long gintc;
@@ -282,6 +368,9 @@ static int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 val)
        if (get_gcsr_flag(id) & INVALID_GCSR)
                return -EINVAL;
 
+       if (id == LOONGARCH_CSR_CPUID)
+               return kvm_set_cpuid(vcpu, val);
+
        if (id == LOONGARCH_CSR_ESTAT) {
                /* ESTAT IP0~IP7 inject through GINTC */
                gintc = (val >> 2) & 0xff;
@@ -924,6 +1013,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
 
        /* Set cpuid */
        kvm_write_sw_gcsr(csr, LOONGARCH_CSR_TMID, vcpu->vcpu_id);
+       kvm_write_sw_gcsr(csr, LOONGARCH_CSR_CPUID, KVM_MAX_PHYID);
 
        /* Start with no pending virtual guest interrupts */
        csr->csrs[LOONGARCH_CSR_GINTC] = 0;
@@ -942,6 +1032,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
 
        hrtimer_cancel(&vcpu->arch.swtimer);
        kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache);
+       kvm_drop_cpuid(vcpu);
        kfree(vcpu->arch.csr);
 
        /*
index 0a37f6fa8f2df03f444dd089ef44fa6efc512179..191d82309a1ed8c58c03abb12b84ee2d5f82c95f 100644 (file)
@@ -30,6 +30,14 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
        if (!kvm->arch.pgd)
                return -ENOMEM;
 
+       kvm->arch.phyid_map = kvzalloc(sizeof(struct kvm_phyid_map), GFP_KERNEL_ACCOUNT);
+       if (!kvm->arch.phyid_map) {
+               free_page((unsigned long)kvm->arch.pgd);
+               kvm->arch.pgd = NULL;
+               return -ENOMEM;
+       }
+       spin_lock_init(&kvm->arch.phyid_map_lock);
+
        kvm_init_vmcs(kvm);
        kvm->arch.gpa_size = BIT(cpu_vabits - 1);
        kvm->arch.root_level = CONFIG_PGTABLE_LEVELS - 1;
@@ -52,6 +60,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
        kvm_destroy_vcpus(kvm);
        free_page((unsigned long)kvm->arch.pgd);
        kvm->arch.pgd = NULL;
+       kvfree(kvm->arch.phyid_map);
+       kvm->arch.phyid_map = NULL;
 }
 
 int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)