aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorMarcelo Tosatti <mtosatti@redhat.com>2014-12-16 09:08:15 -0500
committerPaolo Bonzini <pbonzini@redhat.com>2015-01-08 16:47:30 -0500
commitd0659d946be05e098883b6955d2764595997f6a4 (patch)
tree710980f2f6e1c00c8c2659f366ece12999dd0812 /arch
parent7c6a98dfa1ba9dc64a62e73624ecea9995736bbd (diff)
KVM: x86: add option to advance tscdeadline hrtimer expiration
For the hrtimer which emulates the tscdeadline timer in the guest, add an option to advance expiration, and busy spin on VM-entry waiting for the actual expiration time to elapse. This allows achieving low latencies in cyclictest (or any scenario which requires strict timing regarding timer expiration). Reduces average cyclictest latency from 12us to 8us on Core i5 desktop. Note: this option requires tuning to find the appropriate value for a particular hardware/guest combination. One method is to measure the average delay between apic_timer_fn and VM-entry. Another method is to start with 1000ns, and increase the value in say 500ns increments until avg cyclictest numbers stop decreasing. Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/kvm/lapic.c57
-rw-r--r--arch/x86/kvm/lapic.h3
-rw-r--r--arch/x86/kvm/x86.c5
-rw-r--r--arch/x86/kvm/x86.h2
4 files changed, 66 insertions, 1 deletions
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index fe8bae511e99..e1c0befaa9f6 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -33,6 +33,7 @@
33#include <asm/page.h> 33#include <asm/page.h>
34#include <asm/current.h> 34#include <asm/current.h>
35#include <asm/apicdef.h> 35#include <asm/apicdef.h>
36#include <asm/delay.h>
36#include <linux/atomic.h> 37#include <linux/atomic.h>
37#include <linux/jump_label.h> 38#include <linux/jump_label.h>
38#include "kvm_cache_regs.h" 39#include "kvm_cache_regs.h"
@@ -1073,6 +1074,7 @@ static void apic_timer_expired(struct kvm_lapic *apic)
1073{ 1074{
1074 struct kvm_vcpu *vcpu = apic->vcpu; 1075 struct kvm_vcpu *vcpu = apic->vcpu;
1075 wait_queue_head_t *q = &vcpu->wq; 1076 wait_queue_head_t *q = &vcpu->wq;
1077 struct kvm_timer *ktimer = &apic->lapic_timer;
1076 1078
1077 /* 1079 /*
1078 * Note: KVM_REQ_PENDING_TIMER is implicitly checked in 1080 * Note: KVM_REQ_PENDING_TIMER is implicitly checked in
@@ -1087,11 +1089,61 @@ static void apic_timer_expired(struct kvm_lapic *apic)
1087 1089
1088 if (waitqueue_active(q)) 1090 if (waitqueue_active(q))
1089 wake_up_interruptible(q); 1091 wake_up_interruptible(q);
1092
1093 if (apic_lvtt_tscdeadline(apic))
1094 ktimer->expired_tscdeadline = ktimer->tscdeadline;
1095}
1096
1097/*
1098 * On APICv, this test will cause a busy wait
1099 * during a higher-priority task.
1100 */
1101
1102static bool lapic_timer_int_injected(struct kvm_vcpu *vcpu)
1103{
1104 struct kvm_lapic *apic = vcpu->arch.apic;
1105 u32 reg = kvm_apic_get_reg(apic, APIC_LVTT);
1106
1107 if (kvm_apic_hw_enabled(apic)) {
1108 int vec = reg & APIC_VECTOR_MASK;
1109
1110 if (kvm_x86_ops->test_posted_interrupt)
1111 return kvm_x86_ops->test_posted_interrupt(vcpu, vec);
1112 else {
1113 if (apic_test_vector(vec, apic->regs + APIC_ISR))
1114 return true;
1115 }
1116 }
1117 return false;
1118}
1119
1120void wait_lapic_expire(struct kvm_vcpu *vcpu)
1121{
1122 struct kvm_lapic *apic = vcpu->arch.apic;
1123 u64 guest_tsc, tsc_deadline;
1124
1125 if (!kvm_vcpu_has_lapic(vcpu))
1126 return;
1127
1128 if (apic->lapic_timer.expired_tscdeadline == 0)
1129 return;
1130
1131 if (!lapic_timer_int_injected(vcpu))
1132 return;
1133
1134 tsc_deadline = apic->lapic_timer.expired_tscdeadline;
1135 apic->lapic_timer.expired_tscdeadline = 0;
1136 guest_tsc = kvm_x86_ops->read_l1_tsc(vcpu, native_read_tsc());
1137
1138 /* __delay is delay_tsc whenever the hardware has TSC, thus always. */
1139 if (guest_tsc < tsc_deadline)
1140 __delay(tsc_deadline - guest_tsc);
1090} 1141}
1091 1142
1092static void start_apic_timer(struct kvm_lapic *apic) 1143static void start_apic_timer(struct kvm_lapic *apic)
1093{ 1144{
1094 ktime_t now; 1145 ktime_t now;
1146
1095 atomic_set(&apic->lapic_timer.pending, 0); 1147 atomic_set(&apic->lapic_timer.pending, 0);
1096 1148
1097 if (apic_lvtt_period(apic) || apic_lvtt_oneshot(apic)) { 1149 if (apic_lvtt_period(apic) || apic_lvtt_oneshot(apic)) {
@@ -1137,6 +1189,7 @@ static void start_apic_timer(struct kvm_lapic *apic)
1137 /* lapic timer in tsc deadline mode */ 1189 /* lapic timer in tsc deadline mode */
1138 u64 guest_tsc, tscdeadline = apic->lapic_timer.tscdeadline; 1190 u64 guest_tsc, tscdeadline = apic->lapic_timer.tscdeadline;
1139 u64 ns = 0; 1191 u64 ns = 0;
1192 ktime_t expire;
1140 struct kvm_vcpu *vcpu = apic->vcpu; 1193 struct kvm_vcpu *vcpu = apic->vcpu;
1141 unsigned long this_tsc_khz = vcpu->arch.virtual_tsc_khz; 1194 unsigned long this_tsc_khz = vcpu->arch.virtual_tsc_khz;
1142 unsigned long flags; 1195 unsigned long flags;
@@ -1151,8 +1204,10 @@ static void start_apic_timer(struct kvm_lapic *apic)
1151 if (likely(tscdeadline > guest_tsc)) { 1204 if (likely(tscdeadline > guest_tsc)) {
1152 ns = (tscdeadline - guest_tsc) * 1000000ULL; 1205 ns = (tscdeadline - guest_tsc) * 1000000ULL;
1153 do_div(ns, this_tsc_khz); 1206 do_div(ns, this_tsc_khz);
1207 expire = ktime_add_ns(now, ns);
1208 expire = ktime_sub_ns(expire, lapic_timer_advance_ns);
1154 hrtimer_start(&apic->lapic_timer.timer, 1209 hrtimer_start(&apic->lapic_timer.timer,
1155 ktime_add_ns(now, ns), HRTIMER_MODE_ABS); 1210 expire, HRTIMER_MODE_ABS);
1156 } else 1211 } else
1157 apic_timer_expired(apic); 1212 apic_timer_expired(apic);
1158 1213
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
index c674fce53cf9..7054437944cd 100644
--- a/arch/x86/kvm/lapic.h
+++ b/arch/x86/kvm/lapic.h
@@ -14,6 +14,7 @@ struct kvm_timer {
14 u32 timer_mode; 14 u32 timer_mode;
15 u32 timer_mode_mask; 15 u32 timer_mode_mask;
16 u64 tscdeadline; 16 u64 tscdeadline;
17 u64 expired_tscdeadline;
17 atomic_t pending; /* accumulated triggered timers */ 18 atomic_t pending; /* accumulated triggered timers */
18}; 19};
19 20
@@ -170,4 +171,6 @@ static inline bool kvm_apic_has_events(struct kvm_vcpu *vcpu)
170 171
171bool kvm_apic_pending_eoi(struct kvm_vcpu *vcpu, int vector); 172bool kvm_apic_pending_eoi(struct kvm_vcpu *vcpu, int vector);
172 173
174void wait_lapic_expire(struct kvm_vcpu *vcpu);
175
173#endif 176#endif
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index af9faed270f1..559e3fd6c897 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -108,6 +108,10 @@ EXPORT_SYMBOL_GPL(kvm_max_guest_tsc_khz);
108static u32 tsc_tolerance_ppm = 250; 108static u32 tsc_tolerance_ppm = 250;
109module_param(tsc_tolerance_ppm, uint, S_IRUGO | S_IWUSR); 109module_param(tsc_tolerance_ppm, uint, S_IRUGO | S_IWUSR);
110 110
111/* lapic timer advance (tscdeadline mode only) in nanoseconds */
112unsigned int lapic_timer_advance_ns = 0;
113module_param(lapic_timer_advance_ns, uint, S_IRUGO | S_IWUSR);
114
111static bool backwards_tsc_observed = false; 115static bool backwards_tsc_observed = false;
112 116
113#define KVM_NR_SHARED_MSRS 16 117#define KVM_NR_SHARED_MSRS 16
@@ -6312,6 +6316,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
6312 } 6316 }
6313 6317
6314 trace_kvm_entry(vcpu->vcpu_id); 6318 trace_kvm_entry(vcpu->vcpu_id);
6319 wait_lapic_expire(vcpu);
6315 kvm_x86_ops->run(vcpu); 6320 kvm_x86_ops->run(vcpu);
6316 6321
6317 /* 6322 /*
diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h
index cc1d61af6140..07994f38dacf 100644
--- a/arch/x86/kvm/x86.h
+++ b/arch/x86/kvm/x86.h
@@ -170,5 +170,7 @@ extern u64 kvm_supported_xcr0(void);
170 170
171extern unsigned int min_timer_period_us; 171extern unsigned int min_timer_period_us;
172 172
173extern unsigned int lapic_timer_advance_ns;
174
173extern struct static_key kvm_no_apic_vcpu; 175extern struct static_key kvm_no_apic_vcpu;
174#endif 176#endif