summaryrefslogtreecommitdiffstats
path: root/virt
diff options
context:
space:
mode:
authorChristoffer Dall <christoffer.dall@linaro.org>2015-08-25 13:48:21 -0400
committerChristoffer Dall <christoffer.dall@linaro.org>2015-10-22 17:01:42 -0400
commitd35268da66870d733ae763fd7f9b06a1f63f395e (patch)
tree408e3ad5ebdc30184c149c065aea29d0bc066f07 /virt
parent3217f7c25bca66eed9b07f0b8bfd1937169b0736 (diff)
arm/arm64: KVM: arch_timer: Only schedule soft timer on vcpu_block
We currently schedule a soft timer every time we exit the guest if the timer did not expire while running the guest. This is really not necessary, because the only work we do in the timer work function is to kick the vcpu. Kicking the vcpu does two things: (1) If the vpcu thread is on a waitqueue, make it runnable and remove it from the waitqueue. (2) If the vcpu is running on a different physical CPU from the one doing the kick, it sends a reschedule IPI. The second case cannot happen, because the soft timer is only ever scheduled when the vcpu is not running. The first case is only relevant when the vcpu thread is on a waitqueue, which is only the case when the vcpu thread has called kvm_vcpu_block(). Therefore, we only need to make sure a timer is scheduled for kvm_vcpu_block(), which we do by encapsulating all calls to kvm_vcpu_block() with kvm_timer_{un}schedule calls. Additionally, we only schedule a soft timer if the timer is enabled and unmasked, since it is useless otherwise. Note that theoretically userspace can use the SET_ONE_REG interface to change registers that should cause the timer to fire, even if the vcpu is blocked without a scheduled timer, but this case was not supported before this patch and we leave it for future work for now. Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
Diffstat (limited to 'virt')
-rw-r--r--virt/kvm/arm/arch_timer.c94
1 files changed, 60 insertions, 34 deletions
diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
index b9d3a32cbc04..32095fbb5d7c 100644
--- a/virt/kvm/arm/arch_timer.c
+++ b/virt/kvm/arm/arch_timer.c
@@ -111,14 +111,21 @@ static enum hrtimer_restart kvm_timer_expire(struct hrtimer *hrt)
111 return HRTIMER_NORESTART; 111 return HRTIMER_NORESTART;
112} 112}
113 113
114static bool kvm_timer_irq_can_fire(struct kvm_vcpu *vcpu)
115{
116 struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
117
118 return !(timer->cntv_ctl & ARCH_TIMER_CTRL_IT_MASK) &&
119 (timer->cntv_ctl & ARCH_TIMER_CTRL_ENABLE) &&
120 !kvm_vgic_get_phys_irq_active(timer->map);
121}
122
114bool kvm_timer_should_fire(struct kvm_vcpu *vcpu) 123bool kvm_timer_should_fire(struct kvm_vcpu *vcpu)
115{ 124{
116 struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; 125 struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
117 cycle_t cval, now; 126 cycle_t cval, now;
118 127
119 if ((timer->cntv_ctl & ARCH_TIMER_CTRL_IT_MASK) || 128 if (!kvm_timer_irq_can_fire(vcpu))
120 !(timer->cntv_ctl & ARCH_TIMER_CTRL_ENABLE) ||
121 kvm_vgic_get_phys_irq_active(timer->map))
122 return false; 129 return false;
123 130
124 cval = timer->cntv_cval; 131 cval = timer->cntv_cval;
@@ -127,12 +134,57 @@ bool kvm_timer_should_fire(struct kvm_vcpu *vcpu)
127 return cval <= now; 134 return cval <= now;
128} 135}
129 136
137/*
138 * Schedule the background timer before calling kvm_vcpu_block, so that this
139 * thread is removed from its waitqueue and made runnable when there's a timer
140 * interrupt to handle.
141 */
142void kvm_timer_schedule(struct kvm_vcpu *vcpu)
143{
144 struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
145 u64 ns;
146 cycle_t cval, now;
147
148 BUG_ON(timer_is_armed(timer));
149
150 /*
151 * No need to schedule a background timer if the guest timer has
152 * already expired, because kvm_vcpu_block will return before putting
153 * the thread to sleep.
154 */
155 if (kvm_timer_should_fire(vcpu))
156 return;
157
158 /*
159 * If the timer is not capable of raising interrupts (disabled or
160 * masked), then there's no more work for us to do.
161 */
162 if (!kvm_timer_irq_can_fire(vcpu))
163 return;
164
165 /* The timer has not yet expired, schedule a background timer */
166 cval = timer->cntv_cval;
167 now = kvm_phys_timer_read() - vcpu->kvm->arch.timer.cntvoff;
168
169 ns = cyclecounter_cyc2ns(timecounter->cc,
170 cval - now,
171 timecounter->mask,
172 &timecounter->frac);
173 timer_arm(timer, ns);
174}
175
176void kvm_timer_unschedule(struct kvm_vcpu *vcpu)
177{
178 struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
179 timer_disarm(timer);
180}
181
130/** 182/**
131 * kvm_timer_flush_hwstate - prepare to move the virt timer to the cpu 183 * kvm_timer_flush_hwstate - prepare to move the virt timer to the cpu
132 * @vcpu: The vcpu pointer 184 * @vcpu: The vcpu pointer
133 * 185 *
134 * Disarm any pending soft timers, since the world-switch code will write the 186 * Check if the virtual timer has expired while we were running in the host,
135 * virtual timer state back to the physical CPU. 187 * and inject an interrupt if that was the case.
136 */ 188 */
137void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) 189void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
138{ 190{
@@ -140,17 +192,6 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
140 bool phys_active; 192 bool phys_active;
141 int ret; 193 int ret;
142 194
143 /*
144 * We're about to run this vcpu again, so there is no need to
145 * keep the background timer running, as we're about to
146 * populate the CPU timer again.
147 */
148 timer_disarm(timer);
149
150 /*
151 * If the timer expired while we were not scheduled, now is the time
152 * to inject it.
153 */
154 if (kvm_timer_should_fire(vcpu)) 195 if (kvm_timer_should_fire(vcpu))
155 kvm_timer_inject_irq(vcpu); 196 kvm_timer_inject_irq(vcpu);
156 197
@@ -176,32 +217,17 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
176 * kvm_timer_sync_hwstate - sync timer state from cpu 217 * kvm_timer_sync_hwstate - sync timer state from cpu
177 * @vcpu: The vcpu pointer 218 * @vcpu: The vcpu pointer
178 * 219 *
179 * Check if the virtual timer was armed and either schedule a corresponding 220 * Check if the virtual timer has expired while we were running in the guest,
180 * soft timer or inject directly if already expired. 221 * and inject an interrupt if that was the case.
181 */ 222 */
182void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) 223void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu)
183{ 224{
184 struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; 225 struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
185 cycle_t cval, now;
186 u64 ns;
187 226
188 BUG_ON(timer_is_armed(timer)); 227 BUG_ON(timer_is_armed(timer));
189 228
190 if (kvm_timer_should_fire(vcpu)) { 229 if (kvm_timer_should_fire(vcpu))
191 /*
192 * Timer has already expired while we were not
193 * looking. Inject the interrupt and carry on.
194 */
195 kvm_timer_inject_irq(vcpu); 230 kvm_timer_inject_irq(vcpu);
196 return;
197 }
198
199 cval = timer->cntv_cval;
200 now = kvm_phys_timer_read() - vcpu->kvm->arch.timer.cntvoff;
201
202 ns = cyclecounter_cyc2ns(timecounter->cc, cval - now, timecounter->mask,
203 &timecounter->frac);
204 timer_arm(timer, ns);
205} 231}
206 232
207int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu, 233int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu,