virt inclusion category: feature bugzilla: https://gitcode.com/openeuler/kernel/issues/10066 ---------------------------------------- Keep struct tlbidomain in a global xarray keyed by the kvm pointer instead of embedding it in struct kvm_arch. This leaves the layout of struct kvm_arch untouched and drops the KABI_EXTEND wrapper. Callers look the state up via kvm_tlbidomain(), which costs one RCU-protected xa_load() on the vCPU load path, and must tolerate NULL when the VM has no TLBID support (no hardware support, or failed allocation at VM init). The xarray entry is published at the end of kvm_arm_init_tlbidomain() and erased in free_tlbid_vdomain(), so no entry exists for partially-initialized or unsupported VMs. Signed-off-by: Jinqian Yang <yangjinqian1@huawei.com> --- arch/arm64/include/asm/kvm_host.h | 8 ++- arch/arm64/kvm/arm.c | 115 ++++++++++++++++++++---------- arch/arm64/kvm/sys_regs.c | 16 +++-- 3 files changed, 96 insertions(+), 43 deletions(-) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 29a47406714c..ed2083b7b206 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -252,6 +252,13 @@ struct tlbidomain { struct list_head *pcpu_vcpu_list; }; +/* + * Externally-allocated per-VM TLBID domain state, registered in a global + * xarray keyed by the kvm pointer so that struct kvm_arch stays unchanged + * (no KABI impact). Returns NULL when the VM has no TLBID support. + */ +struct tlbidomain *kvm_tlbidomain(struct kvm *kvm); + struct kvm_arch { struct kvm_s2_mmu mmu; @@ -376,7 +383,6 @@ struct kvm_arch { KABI_EXTEND(u64 midr_el1) KABI_EXTEND(u64 revidr_el1) KABI_EXTEND(u64 aidr_el1) - KABI_EXTEND(struct tlbidomain vdomain) }; struct kvm_vcpu_fault_info { diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index b40c4548c7f1..0025b0499211 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -321,17 +321,28 @@ static int kvm_arm_default_max_vcpus(void) return vgic_present ? kvm_vgic_get_max_vcpus() : KVM_MAX_VCPUS; } +static DEFINE_XARRAY(tlbid_domains); + +struct tlbidomain *kvm_tlbidomain(struct kvm *kvm) +{ + return xa_load(&tlbid_domains, (unsigned long)kvm); +} + static int kvm_arm_init_tlbidomain(struct kvm *kvm) { struct tlbidomain *vdomain; - int i, max_vdomains; + int i, max_vdomains, ret; u64 tlbididr; if (!system_supports_tlbid()) return 0; tlbididr = read_sysreg_s(SYS_TLBIDIDR_EL1); - vdomain = &kvm->arch.vdomain; + + vdomain = kzalloc(sizeof(*vdomain), GFP_KERNEL); + if (!vdomain) + return -ENOMEM; + vdomain->nis = FIELD_GET(TLBIDIDR_EL1_NIS_MASK, tlbididr); vdomain->nvis = FIELD_GET(TLBIDIDR_EL1_NVIS_MASK, tlbididr); vdomain->num_domains = -1; @@ -366,9 +377,9 @@ static int kvm_arm_init_tlbidomain(struct kvm *kvm) } for (i = 0; i < max_vdomains; i++) - kvm->arch.vdomain.domain_map[i] = -1; + vdomain->domain_map[i] = -1; - spin_lock_init(&kvm->arch.vdomain.tlbid_lock); + spin_lock_init(&vdomain->tlbid_lock); vdomain->pcpu_vcpu_list = kcalloc(nr_cpu_ids, sizeof(*vdomain->pcpu_vcpu_list), GFP_KERNEL); @@ -378,7 +389,11 @@ static int kvm_arm_init_tlbidomain(struct kvm *kvm) for (i = 0; i < nr_cpu_ids; i++) INIT_LIST_HEAD(&vdomain->pcpu_vcpu_list[i]); - return 0; + ret = xa_err(xa_store(&tlbid_domains, (unsigned long)kvm, vdomain, + GFP_KERNEL)); + if (!ret) + return 0; + i = max_vdomains; free_vdomain_cpumasks: while (--i >= 0) @@ -388,32 +403,37 @@ static int kvm_arm_init_tlbidomain(struct kvm *kvm) kfree(vdomain->domain_map); destroy_vcpu_data_array: xa_destroy(&vdomain->vcpu_data_array); + kfree(vdomain); return -ENOMEM; } static void free_tlbid_vdomain(struct kvm *kvm) { + struct tlbidomain *vdomain; struct vcpu_tlbid_data *data; int i, max_vdomains; unsigned long index; - max_vdomains = 1 << kvm->arch.vdomain.nvis; + vdomain = xa_erase(&tlbid_domains, (unsigned long)kvm); + if (!vdomain) + return; - if (system_supports_tlbid()) { - kfree(kvm->arch.vdomain.domain_map); + max_vdomains = 1 << vdomain->nvis; + + kfree(vdomain->domain_map); - for (i = 0; i < max_vdomains; i++) - free_cpumask_var(kvm->arch.vdomain.vdomain_cpumasks[i]); - kfree(kvm->arch.vdomain.vdomain_cpumasks); + for (i = 0; i < max_vdomains; i++) + free_cpumask_var(vdomain->vdomain_cpumasks[i]); + kfree(vdomain->vdomain_cpumasks); - kfree(kvm->arch.vdomain.pcpu_vcpu_list); + kfree(vdomain->pcpu_vcpu_list); - xa_for_each(&kvm->arch.vdomain.vcpu_data_array, index, data) { - kfree(data); - } - xa_destroy(&kvm->arch.vdomain.vcpu_data_array); - } + xa_for_each(&vdomain->vcpu_data_array, index, data) + kfree(data); + xa_destroy(&vdomain->vcpu_data_array); + + kfree(vdomain); } /** @@ -860,7 +880,7 @@ static bool is_vcpu_in_vdomain(u32 vdomain_bitmap, int vdomain_idx) static int update_domain_map(struct kvm_vcpu *vcpu, int vdomain_idx, int last_pcpu, bool clear_flag) { - struct tlbidomain *vdomain = &vcpu->kvm->arch.vdomain; + struct tlbidomain *vdomain = kvm_tlbidomain(vcpu->kvm); int pdomain = vdomain->domain_map[vdomain_idx]; cpumask_var_t new_cpus; @@ -940,11 +960,13 @@ static void kvm_arm_update_tlbid_map(struct tlbidomain *vdomain) static void kvm_vcpu_reload_tlbid(struct kvm *kvm) { - if (WARN_ON_ONCE(!kvm->arch.vdomain.kvm_tlbid_enabled)) + struct tlbidomain *vdomain = kvm_tlbidomain(kvm); + + if (WARN_ON_ONCE(!vdomain || !vdomain->kvm_tlbid_enabled)) return; preempt_disable(); - kvm_arm_update_tlbid_map(&kvm->arch.vdomain); + kvm_arm_update_tlbid_map(vdomain); preempt_enable(); } @@ -960,9 +982,9 @@ static inline void set_tlbididr_trap(void) static void kvm_tlbidomain_vcpu_load(struct kvm_vcpu *vcpu) { struct kvm *kvm = vcpu->kvm; - struct tlbidomain *vdomain = &kvm->arch.vdomain; - int max_vdomains = 1 << vdomain->nvis; - DECLARE_BITMAP(clear_bits, max_vdomains); + struct tlbidomain *vdomain = kvm_tlbidomain(kvm); + int max_vdomains; + DECLARE_BITMAP(clear_bits, 32); /* 1 << NVIS, NVIS <= 5 */ struct vcpu_tlbid_data *current_data, *other_data; bool domain_map_changed = false; int i, old_map; @@ -971,9 +993,11 @@ static void kvm_tlbidomain_vcpu_load(struct kvm_vcpu *vcpu) * If we support tlbid, but user does not enable it, we can do * vdomain0 -> pdomainN as well by default. */ - if (!vdomain->kvm_tlbid_enabled) + if (!vdomain || !vdomain->kvm_tlbid_enabled) return; + max_vdomains = 1 << vdomain->nvis; + set_tlbididr_trap(); current_data = xa_load(&vdomain->vcpu_data_array, vcpu->vcpu_idx); @@ -986,7 +1010,7 @@ static void kvm_tlbidomain_vcpu_load(struct kvm_vcpu *vcpu) if (likely(vcpu->cpu == current_data->last_pcpu)) return; - spin_lock(&vcpu->kvm->arch.vdomain.tlbid_lock); + spin_lock(&vdomain->tlbid_lock); /* * Remove current vCPU from its last_pcpu list and compute clear_bits @@ -1075,7 +1099,7 @@ static void kvm_tlbidomain_vcpu_load(struct kvm_vcpu *vcpu) list_add_tail(¤t_data->pcpu_node, &vdomain->pcpu_vcpu_list[vcpu->cpu]); - spin_unlock(&vcpu->kvm->arch.vdomain.tlbid_lock); + spin_unlock(&vdomain->tlbid_lock); } static inline void clear_tlbididr_trap(void) @@ -1090,9 +1114,10 @@ static inline void clear_tlbididr_trap(void) static void kvm_tlbidomain_vcpu_put(struct kvm_vcpu *vcpu) { struct kvm *kvm = vcpu->kvm; + struct tlbidomain *vdomain = kvm_tlbidomain(kvm); int i; - if (!kvm->arch.vdomain.kvm_tlbid_enabled) + if (!vdomain || !vdomain->kvm_tlbid_enabled) return; clear_tlbididr_trap(); @@ -2134,6 +2159,7 @@ static int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, static int kvm_arm_tlbidomain_vcpu_init(struct kvm_vcpu *vcpu) { + struct tlbidomain *vdomain; struct vcpu_tlbid_data *data; int ret; @@ -2157,7 +2183,13 @@ static int kvm_arm_tlbidomain_vcpu_init(struct kvm_vcpu *vcpu) data->last_pcpu = -1; INIT_LIST_HEAD(&data->pcpu_node); - ret = xa_insert(&vcpu->kvm->arch.vdomain.vcpu_data_array, + vdomain = kvm_tlbidomain(vcpu->kvm); + if (!vdomain) { + kfree(data); + return 0; + } + + ret = xa_insert(&vdomain->vcpu_data_array, vcpu->vcpu_idx, data, GFP_KERNEL); if (ret) { kfree(data); @@ -2319,29 +2351,33 @@ static int kvm_arm_vcpu_rmm_psci_complete(struct kvm_vcpu *vcpu, static int kvm_arm_vcpu_set_vdomain(struct kvm_vcpu *vcpu, struct kvm_arm_set_vdomain *vdomain) { + struct tlbidomain *td = kvm_tlbidomain(vcpu->kvm); u32 vdomain_bitmap = vdomain->vdomain_bitmap; u8 num_vdomains = vdomain->num_vdomains; - u8 nvis = vcpu->kvm->arch.vdomain.nvis; - u8 max_vdomains = 1 << nvis; + u8 nvis, max_vdomains; struct vcpu_tlbid_data *data; - int ret; + + if (!td) + return -EINVAL; + + nvis = td->nvis; + max_vdomains = 1 << nvis; if (num_vdomains > max_vdomains) return -EINVAL; - if (vcpu->kvm->arch.vdomain.num_domains != -1 && - vcpu->kvm->arch.vdomain.num_domains != num_vdomains) + if (td->num_domains != -1 && td->num_domains != num_vdomains) return -EINVAL; - data = xa_load(&vcpu->kvm->arch.vdomain.vcpu_data_array, vcpu->vcpu_idx); + data = xa_load(&td->vcpu_data_array, vcpu->vcpu_idx); if (!data) { kvm_err("vCPU%d: Failed to load vcpu_tlbid_data\n", vcpu->vcpu_idx); return -EINVAL; } data->vdomain_bitmap = vdomain_bitmap; - vcpu->kvm->arch.vdomain.num_domains = num_vdomains; - vcpu->kvm->arch.vdomain.guest_tlbid_enabled = true; + td->num_domains = num_vdomains; + td->guest_tlbid_enabled = true; kvm_info("vCPU%d: vDomain bitmap: 0x%x\n", vcpu->vcpu_idx, vdomain_bitmap); @@ -2562,7 +2598,12 @@ static int kvm_vm_set_attr(struct kvm *kvm, struct kvm_device_attr *attr) static int kvm_arm_get_vdomain_num(struct kvm *kvm, struct kvm_arm_get_vdomain *vdomain) { - vdomain->max_vdomains = 1 << kvm->arch.vdomain.nvis; + struct tlbidomain *td = kvm_tlbidomain(kvm); + + if (!td) + return -EINVAL; + + vdomain->max_vdomains = 1 << td->nvis; return 0; } diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 1ef578caa7c0..ec36a6a3262d 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -280,10 +280,12 @@ static bool access_tlbididr(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) { - if (p->is_write) + struct tlbidomain *vdomain = kvm_tlbidomain(vcpu->kvm); + + if (p->is_write || !vdomain) return ignore_write(vcpu, p); - p->regval = vcpu->kvm->arch.vdomain.tlbididr_val; + p->regval = vdomain->tlbididr_val; return true; } @@ -1456,10 +1458,13 @@ static u64 __kvm_read_sanitised_id_reg(const struct kvm_vcpu *vcpu, val &= ID_AA64MMFR3_EL1_TCRX | ID_AA64MMFR3_EL1_S1POE | ID_AA64MMFR3_EL1_S1PIE; break; - case SYS_ID_AA64MMFR4_EL1: - if (!vcpu->kvm->arch.vdomain.guest_tlbid_enabled) + case SYS_ID_AA64MMFR4_EL1: { + struct tlbidomain *vdomain = kvm_tlbidomain(vcpu->kvm); + + if (!vdomain || !vdomain->guest_tlbid_enabled) val &= ~ID_AA64MMFR4_EL1_TLBID; break; + } case SYS_ID_MMFR4_EL1: val &= ~ARM64_FEATURE_MASK(ID_MMFR4_EL1_CCIDX); break; @@ -3792,6 +3797,7 @@ const struct sys_reg_desc *get_reg_by_id(u64 id, */ void kvm_update_aa64mmfr4_tlbid(struct kvm *kvm) { + struct tlbidomain *vdomain = kvm_tlbidomain(kvm); u64 val, tlbid_val; mutex_lock(&kvm->arch.config_lock); @@ -3812,7 +3818,7 @@ void kvm_update_aa64mmfr4_tlbid(struct kvm *kvm) tlbid_val &= ID_AA64MMFR4_EL1_TLBID_MASK; val &= ~ID_AA64MMFR4_EL1_TLBID_MASK; - if (kvm->arch.vdomain.guest_tlbid_enabled) + if (vdomain && vdomain->guest_tlbid_enabled) val |= tlbid_val; kvm_set_vm_id_reg(kvm, SYS_ID_AA64MMFR4_EL1, val); -- 2.33.0