kvm: x86: reduce collisions in mmu_page_hash v4.1.12-110.0.20170822_0730
authorDavid Matlack <dmatlack@google.com>
Mon, 19 Dec 2016 21:58:25 +0000 (13:58 -0800)
committerChuck Anderson <chuck.anderson@oracle.com>
Tue, 22 Aug 2017 18:31:33 +0000 (11:31 -0700)
When using two-dimensional paging, the mmu_page_hash (which provides
lookups for existing kvm_mmu_page structs), becomes imbalanced; with
too many collisions in buckets 0 and 512. This has been seen to cause
mmu_lock to be held for multiple milliseconds in kvm_mmu_get_page on
VMs with a large amount of RAM mapped with 4K pages.

The current hash function uses the lower 10 bits of gfn to index into
mmu_page_hash. When doing shadow paging, gfn is the address of the
guest page table being shadow. These tables are 4K-aligned, which
makes the low bits of gfn a good hash. However, with two-dimensional
paging, no guest page tables are being shadowed, so gfn is the base
address that is mapped by the table. Thus page tables (level=1) have
a 2MB aligned gfn, page directories (level=2) have a 1GB aligned gfn,
etc. This means hashes will only differ in their 10th bit.

hash_64() provides a better hash. For example, on a VM with ~200G
(99458 direct=1 kvm_mmu_page structs):

hash            max_mmu_page_hash_collisions
--------------------------------------------
low 10 bits     49847
hash_64         105
perfect         97

While we're changing the hash, increase the table size by 4x to better
support large VMs (further reduces number of collisions in 200G VM to
29).

Note that hash_64() does not provide a good distribution prior to commit
ef703f49a6c5 ("Eliminate bad hash multipliers from hash_32() and
hash_64()").

Signed-off-by: David Matlack <dmatlack@google.com>
Change-Id: I5aa6b13c834722813c6cca46b8b1ed6f53368ade
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Orabug: 26628797
(cherry picked from commit 114df303a7eeae8b50ebf68229b7e647714a9bea)
Signed-off-by: Govinda Tatti <Govinda.Tatti@Oracle.COM>
Reviewed-by: Jack Vogel <jack.vogel@oracle.com>
Tested-by: Eyal Moscovici <eyal.moscovici@oracle.com> [Ravello/BMCS Team]
arch/x86/include/asm/kvm_host.h
arch/x86/kvm/mmu.c

index 0de00bcde48300452b3fcf076779be03552cbe19..dbe49feb4a337489c97d2aab30d7ca8fc61183fd 100644 (file)
@@ -83,7 +83,7 @@ static inline gfn_t gfn_to_index(gfn_t gfn, gfn_t base_gfn, int level)
 
 #define KVM_PERMILLE_MMU_PAGES 20
 #define KVM_MIN_ALLOC_MMU_PAGES 64
-#define KVM_MMU_HASH_SHIFT 10
+#define KVM_MMU_HASH_SHIFT 12
 #define KVM_NUM_MMU_PAGES (1 << KVM_MMU_HASH_SHIFT)
 #define KVM_MIN_FREE_MMU_PAGES 5
 #define KVM_REFILL_PAGES 25
index 554e877e0bc4a68cc4c60ddcb8402564b30a6bc2..fbf95c74b896df4f7b65db6d92a07f42487b4c7a 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/srcu.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
+#include <linux/hash.h>
 
 #include <asm/page.h>
 #include <asm/cmpxchg.h>
@@ -1624,7 +1625,7 @@ static void kvm_mmu_free_page(struct kvm_mmu_page *sp)
 
 static unsigned kvm_page_table_hashfn(gfn_t gfn)
 {
-       return gfn & ((1 << KVM_MMU_HASH_SHIFT) - 1);
+       return hash_64(gfn, KVM_MMU_HASH_SHIFT);
 }
 
 static void mmu_page_add_parent_pte(struct kvm_vcpu *vcpu,