.. | .. |
---|
178 | 178 | struct kvm_user_return_msrs { |
---|
179 | 179 | struct user_return_notifier urn; |
---|
180 | 180 | bool registered; |
---|
| 181 | + bool dirty; |
---|
181 | 182 | struct kvm_user_return_msr_values { |
---|
182 | 183 | u64 host; |
---|
183 | 184 | u64 curr; |
---|
.. | .. |
---|
295 | 296 | vcpu->arch.apf.gfns[i] = ~0; |
---|
296 | 297 | } |
---|
297 | 298 | |
---|
| 299 | +static void __kvm_on_user_return(struct kvm_user_return_msrs *msrs) |
---|
| 300 | +{ |
---|
| 301 | + struct kvm_user_return_msr_values *values; |
---|
| 302 | + unsigned slot; |
---|
| 303 | + |
---|
| 304 | + if (!msrs->dirty) |
---|
| 305 | + return; |
---|
| 306 | + |
---|
| 307 | + for (slot = 0; slot < user_return_msrs_global.nr; ++slot) { |
---|
| 308 | + values = &msrs->values[slot]; |
---|
| 309 | + if (values->host != values->curr) { |
---|
| 310 | + wrmsrl(user_return_msrs_global.msrs[slot], values->host); |
---|
| 311 | + values->curr = values->host; |
---|
| 312 | + } |
---|
| 313 | + } |
---|
| 314 | + |
---|
| 315 | + msrs->dirty = false; |
---|
| 316 | +} |
---|
| 317 | + |
---|
298 | 318 | static void kvm_on_user_return(struct user_return_notifier *urn) |
---|
299 | 319 | { |
---|
300 | | - unsigned slot; |
---|
301 | 320 | struct kvm_user_return_msrs *msrs |
---|
302 | 321 | = container_of(urn, struct kvm_user_return_msrs, urn); |
---|
303 | | - struct kvm_user_return_msr_values *values; |
---|
304 | 322 | unsigned long flags; |
---|
305 | 323 | |
---|
306 | 324 | /* |
---|
.. | .. |
---|
313 | 331 | user_return_notifier_unregister(urn); |
---|
314 | 332 | } |
---|
315 | 333 | local_irq_restore(flags); |
---|
316 | | - for (slot = 0; slot < user_return_msrs_global.nr; ++slot) { |
---|
317 | | - values = &msrs->values[slot]; |
---|
318 | | - if (values->host != values->curr) { |
---|
319 | | - wrmsrl(user_return_msrs_global.msrs[slot], values->host); |
---|
320 | | - values->curr = values->host; |
---|
321 | | - } |
---|
322 | | - } |
---|
| 334 | + flags = hard_cond_local_irq_save(); |
---|
| 335 | + __kvm_on_user_return(msrs); |
---|
| 336 | + hard_cond_local_irq_restore(flags); |
---|
| 337 | + inband_exit_guest(); |
---|
323 | 338 | } |
---|
324 | 339 | |
---|
325 | 340 | int kvm_probe_user_return_msr(u32 msr) |
---|
.. | .. |
---|
374 | 389 | if (err) |
---|
375 | 390 | return 1; |
---|
376 | 391 | |
---|
| 392 | + msrs->dirty = true; |
---|
377 | 393 | msrs->values[slot].curr = value; |
---|
378 | 394 | if (!msrs->registered) { |
---|
379 | 395 | msrs->urn.on_user_return = kvm_on_user_return; |
---|
.. | .. |
---|
4072 | 4088 | |
---|
4073 | 4089 | void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) |
---|
4074 | 4090 | { |
---|
| 4091 | + struct kvm_user_return_msrs *msrs = this_cpu_ptr(user_return_msrs); |
---|
| 4092 | + unsigned long flags; |
---|
4075 | 4093 | int idx; |
---|
4076 | 4094 | |
---|
4077 | 4095 | if (vcpu->preempted) |
---|
4078 | 4096 | vcpu->arch.preempted_in_kernel = !kvm_x86_ops.get_cpl(vcpu); |
---|
| 4097 | + |
---|
| 4098 | + flags = hard_cond_local_irq_save(); |
---|
| 4099 | + /* |
---|
| 4100 | + * Skip steal time accounting from the out-of-band stage since |
---|
| 4101 | + * this is oob-unsafe. We leave it to the next call from the |
---|
| 4102 | + * inband stage. |
---|
| 4103 | + */ |
---|
| 4104 | + if (running_oob()) |
---|
| 4105 | + goto skip_steal_time_update; |
---|
| 4106 | + |
---|
4079 | 4107 | |
---|
4080 | 4108 | /* |
---|
4081 | 4109 | * Disable page faults because we're in atomic context here. |
---|
.. | .. |
---|
4094 | 4122 | kvm_steal_time_set_preempted(vcpu); |
---|
4095 | 4123 | srcu_read_unlock(&vcpu->kvm->srcu, idx); |
---|
4096 | 4124 | pagefault_enable(); |
---|
| 4125 | +skip_steal_time_update: |
---|
4097 | 4126 | kvm_x86_ops.vcpu_put(vcpu); |
---|
4098 | 4127 | vcpu->arch.last_host_tsc = rdtsc(); |
---|
4099 | 4128 | /* |
---|
.. | .. |
---|
4102 | 4131 | * guest. do_debug expects dr6 to be cleared after it runs, do the same. |
---|
4103 | 4132 | */ |
---|
4104 | 4133 | set_debugreg(0, 6); |
---|
| 4134 | + |
---|
| 4135 | + inband_set_vcpu_release_state(vcpu, false); |
---|
| 4136 | + if (!msrs->dirty) |
---|
| 4137 | + inband_exit_guest(); |
---|
| 4138 | + |
---|
| 4139 | + hard_cond_local_irq_restore(flags); |
---|
4105 | 4140 | } |
---|
| 4141 | + |
---|
| 4142 | +#ifdef CONFIG_DOVETAIL |
---|
| 4143 | +/* hard irqs off. */ |
---|
| 4144 | +void kvm_handle_oob_switch(struct kvm_oob_notifier *nfy) |
---|
| 4145 | +{ |
---|
| 4146 | + struct kvm_user_return_msrs *msrs = this_cpu_ptr(user_return_msrs); |
---|
| 4147 | + struct kvm_vcpu *vcpu; |
---|
| 4148 | + |
---|
| 4149 | + vcpu = container_of(nfy, struct kvm_vcpu, oob_notifier); |
---|
| 4150 | + /* |
---|
| 4151 | + * If user_return MSRs were still active when leaving |
---|
| 4152 | + * kvm_arch_vcpu_put(), inband_exit_guest() was not invoked, |
---|
| 4153 | + * so we might get called later on before kvm_on_user_return() |
---|
| 4154 | + * had a chance to run, if a switch to out-of-band scheduling |
---|
| 4155 | + * sneaks in in the meantime. Prevent kvm_arch_vcpu_put() |
---|
| 4156 | + * from running twice in such a case by checking ->put_vcpu |
---|
| 4157 | + * from the notifier block. |
---|
| 4158 | + */ |
---|
| 4159 | + if (nfy->put_vcpu) |
---|
| 4160 | + kvm_arch_vcpu_put(vcpu); |
---|
| 4161 | + |
---|
| 4162 | + __kvm_on_user_return(msrs); |
---|
| 4163 | + inband_exit_guest(); |
---|
| 4164 | +} |
---|
| 4165 | +#else |
---|
| 4166 | +#define kvm_handle_oob_switch NULL |
---|
| 4167 | +#endif |
---|
4106 | 4168 | |
---|
4107 | 4169 | static int kvm_vcpu_ioctl_get_lapic(struct kvm_vcpu *vcpu, |
---|
4108 | 4170 | struct kvm_lapic_state *s) |
---|
.. | .. |
---|
9142 | 9204 | } |
---|
9143 | 9205 | |
---|
9144 | 9206 | preempt_disable(); |
---|
| 9207 | + local_irq_disable_full(); |
---|
| 9208 | + |
---|
| 9209 | + inband_enter_guest(vcpu); |
---|
| 9210 | + inband_set_vcpu_release_state(vcpu, true); |
---|
9145 | 9211 | |
---|
9146 | 9212 | kvm_x86_ops.prepare_guest_switch(vcpu); |
---|
9147 | 9213 | |
---|
.. | .. |
---|
9150 | 9216 | * IPI are then delayed after guest entry, which ensures that they |
---|
9151 | 9217 | * result in virtual interrupt delivery. |
---|
9152 | 9218 | */ |
---|
9153 | | - local_irq_disable(); |
---|
9154 | 9219 | vcpu->mode = IN_GUEST_MODE; |
---|
9155 | 9220 | |
---|
9156 | 9221 | srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx); |
---|
.. | .. |
---|
9179 | 9244 | if (kvm_vcpu_exit_request(vcpu)) { |
---|
9180 | 9245 | vcpu->mode = OUTSIDE_GUEST_MODE; |
---|
9181 | 9246 | smp_wmb(); |
---|
9182 | | - local_irq_enable(); |
---|
| 9247 | + local_irq_enable_full(); |
---|
9183 | 9248 | preempt_enable(); |
---|
9184 | 9249 | vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu); |
---|
9185 | 9250 | r = 1; |
---|
.. | .. |
---|
9251 | 9316 | * stat.exits increment will do nicely. |
---|
9252 | 9317 | */ |
---|
9253 | 9318 | kvm_before_interrupt(vcpu); |
---|
9254 | | - local_irq_enable(); |
---|
| 9319 | + local_irq_enable_full(); |
---|
9255 | 9320 | ++vcpu->stat.exits; |
---|
9256 | | - local_irq_disable(); |
---|
| 9321 | + local_irq_disable_full(); |
---|
9257 | 9322 | kvm_after_interrupt(vcpu); |
---|
9258 | 9323 | |
---|
9259 | 9324 | /* |
---|
.. | .. |
---|
9273 | 9338 | } |
---|
9274 | 9339 | } |
---|
9275 | 9340 | |
---|
9276 | | - local_irq_enable(); |
---|
| 9341 | + local_irq_enable_full(); |
---|
9277 | 9342 | preempt_enable(); |
---|
9278 | 9343 | |
---|
9279 | 9344 | vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu); |
---|
.. | .. |
---|
9487 | 9552 | /* Swap (qemu) user FPU context for the guest FPU context. */ |
---|
9488 | 9553 | static void kvm_load_guest_fpu(struct kvm_vcpu *vcpu) |
---|
9489 | 9554 | { |
---|
9490 | | - fpregs_lock(); |
---|
| 9555 | + unsigned long flags; |
---|
| 9556 | + |
---|
| 9557 | + flags = fpregs_lock(); |
---|
9491 | 9558 | |
---|
9492 | 9559 | kvm_save_current_fpu(vcpu->arch.user_fpu); |
---|
9493 | 9560 | |
---|
.. | .. |
---|
9496 | 9563 | ~XFEATURE_MASK_PKRU); |
---|
9497 | 9564 | |
---|
9498 | 9565 | fpregs_mark_activate(); |
---|
9499 | | - fpregs_unlock(); |
---|
| 9566 | + fpregs_unlock(flags); |
---|
9500 | 9567 | |
---|
9501 | 9568 | trace_kvm_fpu(1); |
---|
9502 | 9569 | } |
---|
.. | .. |
---|
9504 | 9571 | /* When vcpu_run ends, restore user space FPU context. */ |
---|
9505 | 9572 | static void kvm_put_guest_fpu(struct kvm_vcpu *vcpu) |
---|
9506 | 9573 | { |
---|
9507 | | - fpregs_lock(); |
---|
| 9574 | + unsigned long flags; |
---|
| 9575 | + |
---|
| 9576 | + flags = fpregs_lock(); |
---|
9508 | 9577 | |
---|
9509 | 9578 | kvm_save_current_fpu(vcpu->arch.guest_fpu); |
---|
9510 | 9579 | |
---|
9511 | 9580 | copy_kernel_to_fpregs(&vcpu->arch.user_fpu->state); |
---|
9512 | 9581 | |
---|
9513 | 9582 | fpregs_mark_activate(); |
---|
9514 | | - fpregs_unlock(); |
---|
| 9583 | + fpregs_unlock(flags); |
---|
9515 | 9584 | |
---|
9516 | 9585 | ++vcpu->stat.fpu_reload; |
---|
9517 | 9586 | trace_kvm_fpu(0); |
---|
.. | .. |
---|
10189 | 10258 | if (r) |
---|
10190 | 10259 | goto free_guest_fpu; |
---|
10191 | 10260 | |
---|
| 10261 | + inband_init_vcpu(vcpu, kvm_handle_oob_switch); |
---|
10192 | 10262 | vcpu->arch.arch_capabilities = kvm_get_arch_capabilities(); |
---|
10193 | 10263 | vcpu->arch.msr_platform_info = MSR_PLATFORM_INFO_CPUID_FAULT; |
---|
10194 | 10264 | kvm_vcpu_mtrr_init(vcpu); |
---|