KVM: x86: enable dirty log gradually in small chunks
It could take kvm->mmu_lock for an extended period of time when enabling dirty log for the first time. The main cost is to clear all the D-bits of last level SPTEs. This situation can benefit from manual dirty log protect as well, which can reduce the mmu_lock time taken. The sequence is like this: 1. Initialize all the bits of the dirty bitmap to 1 when enabling dirty log for the first time 2. Only write protect the huge pages 3. KVM_GET_DIRTY_LOG returns the dirty bitmap info 4. KVM_CLEAR_DIRTY_LOG will clear D-bit for each of the leaf level SPTEs gradually in small chunks Under the Intel(R) Xeon(R) Gold 6152 CPU @ 2.10GHz environment, I did some tests with a 128G windows VM and counted the time taken of memory_global_dirty_log_start, here is the numbers: VM Size Before After optimization 128G 460ms 10ms Signed-off-by: Jay Zhou <jianjay.zhou@huawei.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
@@ -858,7 +858,7 @@ static int kvm_vm_release(struct inode *inode, struct file *filp)
|
||||
* Allocation size is twice as large as the actual dirty bitmap size.
|
||||
* See kvm_vm_ioctl_get_dirty_log() why this is needed.
|
||||
*/
|
||||
static int kvm_create_dirty_bitmap(struct kvm_memory_slot *memslot)
|
||||
static int kvm_alloc_dirty_bitmap(struct kvm_memory_slot *memslot)
|
||||
{
|
||||
unsigned long dirty_bytes = 2 * kvm_dirty_bitmap_bytes(memslot);
|
||||
|
||||
@@ -1288,9 +1288,12 @@ int __kvm_set_memory_region(struct kvm *kvm,
|
||||
if (!(new.flags & KVM_MEM_LOG_DIRTY_PAGES))
|
||||
new.dirty_bitmap = NULL;
|
||||
else if (!new.dirty_bitmap) {
|
||||
r = kvm_create_dirty_bitmap(&new);
|
||||
r = kvm_alloc_dirty_bitmap(&new);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (kvm_dirty_log_manual_protect_and_init_set(kvm))
|
||||
bitmap_set(new.dirty_bitmap, 0, new.npages);
|
||||
}
|
||||
|
||||
r = kvm_set_memslot(kvm, mem, &old, &new, as_id, change);
|
||||
@@ -3529,9 +3532,6 @@ static long kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg)
|
||||
case KVM_CAP_IOEVENTFD_ANY_LENGTH:
|
||||
case KVM_CAP_CHECK_EXTENSION_VM:
|
||||
case KVM_CAP_ENABLE_CAP_VM:
|
||||
#ifdef CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT
|
||||
case KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2:
|
||||
#endif
|
||||
return 1;
|
||||
#ifdef CONFIG_KVM_MMIO
|
||||
case KVM_CAP_COALESCED_MMIO:
|
||||
@@ -3539,6 +3539,10 @@ static long kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg)
|
||||
case KVM_CAP_COALESCED_PIO:
|
||||
return 1;
|
||||
#endif
|
||||
#ifdef CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT
|
||||
case KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2:
|
||||
return KVM_DIRTY_LOG_MANUAL_CAPS;
|
||||
#endif
|
||||
#ifdef CONFIG_HAVE_KVM_IRQ_ROUTING
|
||||
case KVM_CAP_IRQ_ROUTING:
|
||||
return KVM_MAX_IRQ_ROUTES;
|
||||
@@ -3566,11 +3570,17 @@ static int kvm_vm_ioctl_enable_cap_generic(struct kvm *kvm,
|
||||
{
|
||||
switch (cap->cap) {
|
||||
#ifdef CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT
|
||||
case KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2:
|
||||
if (cap->flags || (cap->args[0] & ~1))
|
||||
case KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2: {
|
||||
u64 allowed_options = KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE;
|
||||
|
||||
if (cap->args[0] & KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE)
|
||||
allowed_options = KVM_DIRTY_LOG_MANUAL_CAPS;
|
||||
|
||||
if (cap->flags || (cap->args[0] & ~allowed_options))
|
||||
return -EINVAL;
|
||||
kvm->manual_dirty_log_protect = cap->args[0];
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
return kvm_vm_ioctl_enable_cap(kvm, cap);
|
||||
|
Reference in New Issue
Block a user