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
Showing the changes to include/linux (1 of 8 files). Show all
Unified · Split
| @@ -360,6 +360,10 @@ static inline unsigned long *kvm_second_dirty_bitmap(struct kvm_memory_slot *mem · 359 unmodified lines | ||
| 360 | 360 | return memslot->dirty_bitmap + len / sizeof(*memslot->dirty_bitmap); |
| 361 | 361 | } |
| 362 | 362 | |
| 363 | +#ifndef KVM_DIRTY_LOG_MANUAL_CAPS | |
| 364 | +#define KVM_DIRTY_LOG_MANUAL_CAPS KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | |
| 365 | +#endif | |
| 366 | + | |
| 363 | 367 | struct kvm_s390_adapter_int { |
| 364 | 368 | u64 ind_addr; |
| 365 | 369 | u64 summary_addr; |
| @@ -493,7 +497,7 @@ struct kvm { · 127 unmodified lines | ||
| 493 | 497 | #endif |
| 494 | 498 | long tlbs_dirty; |
| 495 | 499 | struct list_head devices; |
| 496 | − bool manual_dirty_log_protect; | |
| 500 | + u64 manual_dirty_log_protect; | |
| 497 | 501 | struct dentry *debugfs_dentry; |
| 498 | 502 | struct kvm_stat_data **debugfs_stat_data; |
| 499 | 503 | struct srcu_struct srcu; |
| @@ -527,6 +531,11 @@ struct kvm { · 27 unmodified lines | ||
| 527 | 531 | #define vcpu_err(vcpu, fmt, ...) \ |
| 528 | 532 | kvm_err("vcpu%i " fmt, (vcpu)->vcpu_id, ## __VA_ARGS__) |
| 529 | 533 | |
| 534 | +static inline bool kvm_dirty_log_manual_protect_and_init_set(struct kvm *kvm) | |
| 535 | +{ | |
| 536 | + return !!(kvm->manual_dirty_log_protect & KVM_DIRTY_LOG_INITIALLY_SET); | |
| 537 | +} | |
| 538 | + | |
| 530 | 539 | static inline struct kvm_io_bus *kvm_get_bus(struct kvm *kvm, enum kvm_bus idx) |
| 531 | 540 | { |
| 532 | 541 | return srcu_dereference_check(kvm->buses[idx], &kvm->srcu, |