diff options
author | Wanpeng Li <wanpeng.li@hotmail.com> | 2015-09-03 10:07:38 -0400 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2015-09-06 10:32:43 -0400 |
commit | aca6ff29c4063a8d467cdee241e6b3bf7dc4a171 (patch) | |
tree | e006ea14ead8ae95add04f135dd332bdcbea4bdf | |
parent | 19020f8ab83de9dc5a9c8af1f321a526f38bbc40 (diff) |
KVM: dynamic halt-polling
There is a downside of always-poll since poll is still happened for idle
vCPUs which can waste cpu usage. This patchset add the ability to adjust
halt_poll_ns dynamically, to grow halt_poll_ns when shot halt is detected,
and to shrink halt_poll_ns when long halt is detected.
There are two new kernel parameters for changing the halt_poll_ns:
halt_poll_ns_grow and halt_poll_ns_shrink.
no-poll always-poll dynamic-poll
-----------------------------------------------------------------------
Idle (nohz) vCPU %c0 0.15% 0.3% 0.2%
Idle (250HZ) vCPU %c0 1.1% 4.6%~14% 1.2%
TCP_RR latency 34us 27us 26.7us
"Idle (X) vCPU %c0" is the percent of time the physical cpu spent in
c0 over 60 seconds (each vCPU is pinned to a pCPU). (nohz) means the
guest was tickless. (250HZ) means the guest was ticking at 250HZ.
The big win is with ticking operating systems. Running the linux guest
with nohz=off (and HZ=250), we save 3.4%~12.8% CPUs/second and get close
to no-polling overhead levels by using the dynamic-poll. The savings
should be even higher for higher frequency ticks.
Suggested-by: David Matlack <dmatlack@google.com>
Signed-off-by: Wanpeng Li <wanpeng.li@hotmail.com>
[Simplify the patch. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rw-r--r-- | virt/kvm/kvm_main.c | 53 |
1 files changed, 51 insertions, 2 deletions
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index c06e57cd1269..8ab49cfc024e 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c | |||
@@ -66,9 +66,18 @@ | |||
66 | MODULE_AUTHOR("Qumranet"); | 66 | MODULE_AUTHOR("Qumranet"); |
67 | MODULE_LICENSE("GPL"); | 67 | MODULE_LICENSE("GPL"); |
68 | 68 | ||
69 | static unsigned int halt_poll_ns; | 69 | /* halt polling only reduces halt latency by 5-7 us, 500us is enough */ |
70 | static unsigned int halt_poll_ns = 500000; | ||
70 | module_param(halt_poll_ns, uint, S_IRUGO | S_IWUSR); | 71 | module_param(halt_poll_ns, uint, S_IRUGO | S_IWUSR); |
71 | 72 | ||
73 | /* Default doubles per-vcpu halt_poll_ns. */ | ||
74 | static unsigned int halt_poll_ns_grow = 2; | ||
75 | module_param(halt_poll_ns_grow, int, S_IRUGO); | ||
76 | |||
77 | /* Default resets per-vcpu halt_poll_ns . */ | ||
78 | static unsigned int halt_poll_ns_shrink; | ||
79 | module_param(halt_poll_ns_shrink, int, S_IRUGO); | ||
80 | |||
72 | /* | 81 | /* |
73 | * Ordering of locks: | 82 | * Ordering of locks: |
74 | * | 83 | * |
@@ -1907,6 +1916,31 @@ void kvm_vcpu_mark_page_dirty(struct kvm_vcpu *vcpu, gfn_t gfn) | |||
1907 | } | 1916 | } |
1908 | EXPORT_SYMBOL_GPL(kvm_vcpu_mark_page_dirty); | 1917 | EXPORT_SYMBOL_GPL(kvm_vcpu_mark_page_dirty); |
1909 | 1918 | ||
1919 | static void grow_halt_poll_ns(struct kvm_vcpu *vcpu) | ||
1920 | { | ||
1921 | int val = vcpu->halt_poll_ns; | ||
1922 | |||
1923 | /* 10us base */ | ||
1924 | if (val == 0 && halt_poll_ns_grow) | ||
1925 | val = 10000; | ||
1926 | else | ||
1927 | val *= halt_poll_ns_grow; | ||
1928 | |||
1929 | vcpu->halt_poll_ns = val; | ||
1930 | } | ||
1931 | |||
1932 | static void shrink_halt_poll_ns(struct kvm_vcpu *vcpu) | ||
1933 | { | ||
1934 | int val = vcpu->halt_poll_ns; | ||
1935 | |||
1936 | if (halt_poll_ns_shrink == 0) | ||
1937 | val = 0; | ||
1938 | else | ||
1939 | val /= halt_poll_ns_shrink; | ||
1940 | |||
1941 | vcpu->halt_poll_ns = val; | ||
1942 | } | ||
1943 | |||
1910 | static int kvm_vcpu_check_block(struct kvm_vcpu *vcpu) | 1944 | static int kvm_vcpu_check_block(struct kvm_vcpu *vcpu) |
1911 | { | 1945 | { |
1912 | if (kvm_arch_vcpu_runnable(vcpu)) { | 1946 | if (kvm_arch_vcpu_runnable(vcpu)) { |
@@ -1929,6 +1963,7 @@ void kvm_vcpu_block(struct kvm_vcpu *vcpu) | |||
1929 | ktime_t start, cur; | 1963 | ktime_t start, cur; |
1930 | DEFINE_WAIT(wait); | 1964 | DEFINE_WAIT(wait); |
1931 | bool waited = false; | 1965 | bool waited = false; |
1966 | u64 block_ns; | ||
1932 | 1967 | ||
1933 | start = cur = ktime_get(); | 1968 | start = cur = ktime_get(); |
1934 | if (vcpu->halt_poll_ns) { | 1969 | if (vcpu->halt_poll_ns) { |
@@ -1961,7 +1996,21 @@ void kvm_vcpu_block(struct kvm_vcpu *vcpu) | |||
1961 | cur = ktime_get(); | 1996 | cur = ktime_get(); |
1962 | 1997 | ||
1963 | out: | 1998 | out: |
1964 | trace_kvm_vcpu_wakeup(ktime_to_ns(cur) - ktime_to_ns(start), waited); | 1999 | block_ns = ktime_to_ns(cur) - ktime_to_ns(start); |
2000 | |||
2001 | if (halt_poll_ns) { | ||
2002 | if (block_ns <= vcpu->halt_poll_ns) | ||
2003 | ; | ||
2004 | /* we had a long block, shrink polling */ | ||
2005 | else if (vcpu->halt_poll_ns && block_ns > halt_poll_ns) | ||
2006 | shrink_halt_poll_ns(vcpu); | ||
2007 | /* we had a short halt and our poll time is too small */ | ||
2008 | else if (vcpu->halt_poll_ns < halt_poll_ns && | ||
2009 | block_ns < halt_poll_ns) | ||
2010 | grow_halt_poll_ns(vcpu); | ||
2011 | } | ||
2012 | |||
2013 | trace_kvm_vcpu_wakeup(block_ns, waited); | ||
1965 | } | 2014 | } |
1966 | EXPORT_SYMBOL_GPL(kvm_vcpu_block); | 2015 | EXPORT_SYMBOL_GPL(kvm_vcpu_block); |
1967 | 2016 | ||