mirror of
https://github.com/torvalds/linux.git
synced 2026-05-24 23:22:31 +02:00
KVM: x86: Make cpu_dirty_log_size a per-VM value
Make cpu_dirty_log_size (CPU's dirty log buffer size) a per-VM value and set the per-VM cpu_dirty_log_size only for normal VMs when PML is enabled. Do not set it for TDs. Until now, cpu_dirty_log_size was a system-wide value that is used for all VMs and is set to the PML buffer size when PML was enabled in VMX. However, PML is not currently supported for TDs, though PML remains available for normal VMs as long as the feature is supported by hardware and enabled in VMX. Making cpu_dirty_log_size a per-VM value allows it to be ther PML buffer size for normal VMs and 0 for TDs. This allows functions like kvm_arch_sync_dirty_log() and kvm_mmu_update_cpu_dirty_logging() to determine if PML is supported, in order to kick off vCPUs or request them to update CPU dirty logging status (turn on/off PML in VMCS). This fixes an issue first reported in [1], where QEMU attaches an emulated VGA device to a TD; note that KVM_MEM_LOG_DIRTY_PAGES still works if the corresponding has no flag KVM_MEM_GUEST_MEMFD. KVM then invokes kvm_mmu_update_cpu_dirty_logging() and from there vmx_update_cpu_dirty_logging(), which incorrectly accesses a kvm_vmx struct for a TDX VM. Reported-by: ANAND NARSHINHA PATIL <Anand.N.Patil@ibm.com> Reported-by: Pedro Principeza <pedro.principeza@canonical.com> Reported-by: Farrah Chen <farrah.chen@intel.com> Closes: https://github.com/canonical/tdx/issues/202 Link: https://github.com/canonical/tdx/issues/202 [1] Suggested-by: Kai Huang <kai.huang@intel.com> Signed-off-by: Yan Zhao <yan.y.zhao@intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
fd3252571b
commit
fbb4adadea
|
|
@ -1562,6 +1562,13 @@ struct kvm_arch {
|
|||
struct kvm_mmu_memory_cache split_desc_cache;
|
||||
|
||||
gfn_t gfn_direct_bits;
|
||||
|
||||
/*
|
||||
* Size of the CPU's dirty log buffer, i.e. VMX's PML buffer. A Zero
|
||||
* value indicates CPU dirty logging is unsupported or disabled in
|
||||
* current VM.
|
||||
*/
|
||||
int cpu_dirty_log_size;
|
||||
};
|
||||
|
||||
struct kvm_vm_stat {
|
||||
|
|
@ -1815,11 +1822,6 @@ struct kvm_x86_ops {
|
|||
struct x86_exception *exception);
|
||||
void (*handle_exit_irqoff)(struct kvm_vcpu *vcpu);
|
||||
|
||||
/*
|
||||
* Size of the CPU's dirty log buffer, i.e. VMX's PML buffer. A zero
|
||||
* value indicates CPU dirty logging is unsupported or disabled.
|
||||
*/
|
||||
int cpu_dirty_log_size;
|
||||
void (*update_cpu_dirty_logging)(struct kvm_vcpu *vcpu);
|
||||
|
||||
const struct kvm_x86_nested_ops *nested_ops;
|
||||
|
|
|
|||
|
|
@ -1305,7 +1305,7 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
|
|||
* enabled but it chooses between clearing the Dirty bit and Writeable
|
||||
* bit based on the context.
|
||||
*/
|
||||
if (kvm_x86_ops.cpu_dirty_log_size)
|
||||
if (kvm->arch.cpu_dirty_log_size)
|
||||
kvm_mmu_clear_dirty_pt_masked(kvm, slot, gfn_offset, mask);
|
||||
else
|
||||
kvm_mmu_write_protect_pt_masked(kvm, slot, gfn_offset, mask);
|
||||
|
|
@ -1313,7 +1313,7 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
|
|||
|
||||
int kvm_cpu_dirty_log_size(struct kvm *kvm)
|
||||
{
|
||||
return kvm_x86_ops.cpu_dirty_log_size;
|
||||
return kvm->arch.cpu_dirty_log_size;
|
||||
}
|
||||
|
||||
bool kvm_mmu_slot_gfn_write_protect(struct kvm *kvm,
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ static inline bool kvm_mmu_page_ad_need_write_protect(struct kvm *kvm,
|
|||
* being enabled is mandatory as the bits used to denote WP-only SPTEs
|
||||
* are reserved for PAE paging (32-bit KVM).
|
||||
*/
|
||||
return kvm_x86_ops.cpu_dirty_log_size && sp->role.guest_mode;
|
||||
return kvm->arch.cpu_dirty_log_size && sp->role.guest_mode;
|
||||
}
|
||||
|
||||
static inline gfn_t gfn_round_for_level(gfn_t gfn, int level)
|
||||
|
|
|
|||
|
|
@ -322,7 +322,6 @@ struct kvm_x86_ops vt_x86_ops __initdata = {
|
|||
.check_intercept = vmx_check_intercept,
|
||||
.handle_exit_irqoff = vmx_handle_exit_irqoff,
|
||||
|
||||
.cpu_dirty_log_size = PML_LOG_NR_ENTRIES,
|
||||
.update_cpu_dirty_logging = vmx_update_cpu_dirty_logging,
|
||||
|
||||
.nested_ops = &vmx_nested_ops,
|
||||
|
|
|
|||
|
|
@ -7649,6 +7649,9 @@ int vmx_vm_init(struct kvm *kvm)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (enable_pml)
|
||||
kvm->arch.cpu_dirty_log_size = PML_LOG_NR_ENTRIES;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -8502,9 +8505,6 @@ __init int vmx_hardware_setup(void)
|
|||
if (!enable_ept || !enable_ept_ad_bits || !cpu_has_vmx_pml())
|
||||
enable_pml = 0;
|
||||
|
||||
if (!enable_pml)
|
||||
vt_x86_ops.cpu_dirty_log_size = 0;
|
||||
|
||||
if (!cpu_has_vmx_preemption_timer())
|
||||
enable_preemption_timer = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -6480,7 +6480,7 @@ void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)
|
|||
struct kvm_vcpu *vcpu;
|
||||
unsigned long i;
|
||||
|
||||
if (!kvm_x86_ops.cpu_dirty_log_size)
|
||||
if (!kvm->arch.cpu_dirty_log_size)
|
||||
return;
|
||||
|
||||
kvm_for_each_vcpu(i, vcpu, kvm)
|
||||
|
|
@ -13078,7 +13078,7 @@ static void kvm_mmu_update_cpu_dirty_logging(struct kvm *kvm, bool enable)
|
|||
{
|
||||
int nr_slots;
|
||||
|
||||
if (!kvm_x86_ops.cpu_dirty_log_size)
|
||||
if (!kvm->arch.cpu_dirty_log_size)
|
||||
return;
|
||||
|
||||
nr_slots = atomic_read(&kvm->nr_memslots_dirty_logging);
|
||||
|
|
@ -13150,7 +13150,7 @@ static void kvm_mmu_slot_apply_flags(struct kvm *kvm,
|
|||
if (READ_ONCE(eager_page_split))
|
||||
kvm_mmu_slot_try_split_huge_pages(kvm, new, PG_LEVEL_4K);
|
||||
|
||||
if (kvm_x86_ops.cpu_dirty_log_size) {
|
||||
if (kvm->arch.cpu_dirty_log_size) {
|
||||
kvm_mmu_slot_leaf_clear_dirty(kvm, new);
|
||||
kvm_mmu_slot_remove_write_access(kvm, new, PG_LEVEL_2M);
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user