mirror of
https://github.com/torvalds/linux.git
synced 2024-11-27 06:31:52 +00:00
KVM: x86/xen: handle PV timers oneshot mode
If the guest has offloaded the timer virq, handle the following hypercalls for programming the timer: VCPUOP_set_singleshot_timer VCPUOP_stop_singleshot_timer set_timer_op(timestamp_ns) The event channel corresponding to the timer virq is then used to inject events once timer deadlines are met. For now we back the PV timer with hrtimer. [ dwmw2: Add save/restore, 32-bit compat mode, immediate delivery, don't check timer in kvm_vcpu_has_event() ] Signed-off-by: Joao Martins <joao.m.martins@oracle.com> Signed-off-by: David Woodhouse <dwmw@amazon.co.uk> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Message-Id: <20220303154127.202856-13-dwmw2@infradead.org> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
942c2490c2
commit
5363952605
@ -614,6 +614,10 @@ struct kvm_vcpu_xen {
|
|||||||
u64 runstate_times[4];
|
u64 runstate_times[4];
|
||||||
unsigned long evtchn_pending_sel;
|
unsigned long evtchn_pending_sel;
|
||||||
u32 vcpu_id; /* The Xen / ACPI vCPU ID */
|
u32 vcpu_id; /* The Xen / ACPI vCPU ID */
|
||||||
|
u32 timer_virq;
|
||||||
|
u64 timer_expires; /* In guest epoch */
|
||||||
|
atomic_t timer_pending;
|
||||||
|
struct hrtimer timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct kvm_vcpu_arch {
|
struct kvm_vcpu_arch {
|
||||||
|
@ -22,10 +22,14 @@
|
|||||||
*/
|
*/
|
||||||
int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
|
int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
if (lapic_in_kernel(vcpu))
|
int r = 0;
|
||||||
return apic_has_pending_timer(vcpu);
|
|
||||||
|
|
||||||
return 0;
|
if (lapic_in_kernel(vcpu))
|
||||||
|
r = apic_has_pending_timer(vcpu);
|
||||||
|
if (kvm_xen_timer_enabled(vcpu))
|
||||||
|
r += kvm_xen_has_pending_timer(vcpu);
|
||||||
|
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(kvm_cpu_has_pending_timer);
|
EXPORT_SYMBOL(kvm_cpu_has_pending_timer);
|
||||||
|
|
||||||
@ -143,6 +147,8 @@ void kvm_inject_pending_timer_irqs(struct kvm_vcpu *vcpu)
|
|||||||
{
|
{
|
||||||
if (lapic_in_kernel(vcpu))
|
if (lapic_in_kernel(vcpu))
|
||||||
kvm_inject_apic_timer_irqs(vcpu);
|
kvm_inject_apic_timer_irqs(vcpu);
|
||||||
|
if (kvm_xen_timer_enabled(vcpu))
|
||||||
|
kvm_xen_inject_timer_irqs(vcpu);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(kvm_inject_pending_timer_irqs);
|
EXPORT_SYMBOL_GPL(kvm_inject_pending_timer_irqs);
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
|
static int kvm_xen_set_evtchn(struct kvm_xen_evtchn *xe, struct kvm *kvm);
|
||||||
static int kvm_xen_setattr_evtchn(struct kvm *kvm, struct kvm_xen_hvm_attr *data);
|
static int kvm_xen_setattr_evtchn(struct kvm *kvm, struct kvm_xen_hvm_attr *data);
|
||||||
static bool kvm_xen_hcall_evtchn_send(struct kvm_vcpu *vcpu, u64 param, u64 *r);
|
static bool kvm_xen_hcall_evtchn_send(struct kvm_vcpu *vcpu, u64 param, u64 *r);
|
||||||
|
|
||||||
@ -108,6 +109,66 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kvm_xen_inject_timer_irqs(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
if (atomic_read(&vcpu->arch.xen.timer_pending) > 0) {
|
||||||
|
struct kvm_xen_evtchn e;
|
||||||
|
|
||||||
|
e.vcpu_id = vcpu->vcpu_id;
|
||||||
|
e.vcpu_idx = vcpu->vcpu_idx;
|
||||||
|
e.port = vcpu->arch.xen.timer_virq;
|
||||||
|
e.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL;
|
||||||
|
|
||||||
|
kvm_xen_set_evtchn(&e, vcpu->kvm);
|
||||||
|
|
||||||
|
vcpu->arch.xen.timer_expires = 0;
|
||||||
|
atomic_set(&vcpu->arch.xen.timer_pending, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum hrtimer_restart xen_timer_callback(struct hrtimer *timer)
|
||||||
|
{
|
||||||
|
struct kvm_vcpu *vcpu = container_of(timer, struct kvm_vcpu,
|
||||||
|
arch.xen.timer);
|
||||||
|
if (atomic_read(&vcpu->arch.xen.timer_pending))
|
||||||
|
return HRTIMER_NORESTART;
|
||||||
|
|
||||||
|
atomic_inc(&vcpu->arch.xen.timer_pending);
|
||||||
|
kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
|
||||||
|
kvm_vcpu_kick(vcpu);
|
||||||
|
|
||||||
|
return HRTIMER_NORESTART;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_xen_start_timer(struct kvm_vcpu *vcpu, u64 guest_abs, s64 delta_ns)
|
||||||
|
{
|
||||||
|
atomic_set(&vcpu->arch.xen.timer_pending, 0);
|
||||||
|
vcpu->arch.xen.timer_expires = guest_abs;
|
||||||
|
|
||||||
|
if (delta_ns <= 0) {
|
||||||
|
xen_timer_callback(&vcpu->arch.xen.timer);
|
||||||
|
} else {
|
||||||
|
ktime_t ktime_now = ktime_get();
|
||||||
|
hrtimer_start(&vcpu->arch.xen.timer,
|
||||||
|
ktime_add_ns(ktime_now, delta_ns),
|
||||||
|
HRTIMER_MODE_ABS_HARD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_xen_stop_timer(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
hrtimer_cancel(&vcpu->arch.xen.timer);
|
||||||
|
vcpu->arch.xen.timer_expires = 0;
|
||||||
|
atomic_set(&vcpu->arch.xen.timer_pending, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_xen_init_timer(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
hrtimer_init(&vcpu->arch.xen.timer, CLOCK_MONOTONIC,
|
||||||
|
HRTIMER_MODE_ABS_HARD);
|
||||||
|
vcpu->arch.xen.timer.function = xen_timer_callback;
|
||||||
|
}
|
||||||
|
|
||||||
static void kvm_xen_update_runstate(struct kvm_vcpu *v, int state)
|
static void kvm_xen_update_runstate(struct kvm_vcpu *v, int state)
|
||||||
{
|
{
|
||||||
struct kvm_vcpu_xen *vx = &v->arch.xen;
|
struct kvm_vcpu_xen *vx = &v->arch.xen;
|
||||||
@ -612,6 +673,28 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case KVM_XEN_VCPU_ATTR_TYPE_TIMER:
|
||||||
|
if (data->u.timer.port) {
|
||||||
|
if (data->u.timer.priority != KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL) {
|
||||||
|
r = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vcpu->arch.xen.timer_virq = data->u.timer.port;
|
||||||
|
kvm_xen_init_timer(vcpu);
|
||||||
|
|
||||||
|
/* Restart the timer if it's set */
|
||||||
|
if (data->u.timer.expires_ns)
|
||||||
|
kvm_xen_start_timer(vcpu, data->u.timer.expires_ns,
|
||||||
|
data->u.timer.expires_ns -
|
||||||
|
get_kvmclock_ns(vcpu->kvm));
|
||||||
|
} else if (kvm_xen_timer_enabled(vcpu)) {
|
||||||
|
kvm_xen_stop_timer(vcpu);
|
||||||
|
vcpu->arch.xen.timer_virq = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -692,6 +775,13 @@ int kvm_xen_vcpu_get_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
|
|||||||
r = 0;
|
r = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case KVM_XEN_VCPU_ATTR_TYPE_TIMER:
|
||||||
|
data->u.timer.port = vcpu->arch.xen.timer_virq;
|
||||||
|
data->u.timer.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL;
|
||||||
|
data->u.timer.expires_ns = vcpu->arch.xen.timer_expires;
|
||||||
|
r = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -827,6 +917,112 @@ static bool kvm_xen_hcall_sched_op(struct kvm_vcpu *vcpu, int cmd, u64 param, u6
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct compat_vcpu_set_singleshot_timer {
|
||||||
|
uint64_t timeout_abs_ns;
|
||||||
|
uint32_t flags;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool longmode, int cmd,
|
||||||
|
int vcpu_id, u64 param, u64 *r)
|
||||||
|
{
|
||||||
|
struct vcpu_set_singleshot_timer oneshot;
|
||||||
|
s64 delta;
|
||||||
|
gpa_t gpa;
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
if (!kvm_xen_timer_enabled(vcpu))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case VCPUOP_set_singleshot_timer:
|
||||||
|
if (vcpu->arch.xen.vcpu_id != vcpu_id) {
|
||||||
|
*r = -EINVAL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
idx = srcu_read_lock(&vcpu->kvm->srcu);
|
||||||
|
gpa = kvm_mmu_gva_to_gpa_system(vcpu, param, NULL);
|
||||||
|
srcu_read_unlock(&vcpu->kvm->srcu, idx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The only difference for 32-bit compat is the 4 bytes of
|
||||||
|
* padding after the interesting part of the structure. So
|
||||||
|
* for a faithful emulation of Xen we have to *try* to copy
|
||||||
|
* the padding and return -EFAULT if we can't. Otherwise we
|
||||||
|
* might as well just have copied the 12-byte 32-bit struct.
|
||||||
|
*/
|
||||||
|
BUILD_BUG_ON(offsetof(struct compat_vcpu_set_singleshot_timer, timeout_abs_ns) !=
|
||||||
|
offsetof(struct vcpu_set_singleshot_timer, timeout_abs_ns));
|
||||||
|
BUILD_BUG_ON(sizeof_field(struct compat_vcpu_set_singleshot_timer, timeout_abs_ns) !=
|
||||||
|
sizeof_field(struct vcpu_set_singleshot_timer, timeout_abs_ns));
|
||||||
|
BUILD_BUG_ON(offsetof(struct compat_vcpu_set_singleshot_timer, flags) !=
|
||||||
|
offsetof(struct vcpu_set_singleshot_timer, flags));
|
||||||
|
BUILD_BUG_ON(sizeof_field(struct compat_vcpu_set_singleshot_timer, flags) !=
|
||||||
|
sizeof_field(struct vcpu_set_singleshot_timer, flags));
|
||||||
|
|
||||||
|
if (!gpa ||
|
||||||
|
kvm_vcpu_read_guest(vcpu, gpa, &oneshot, longmode ? sizeof(oneshot) :
|
||||||
|
sizeof(struct compat_vcpu_set_singleshot_timer))) {
|
||||||
|
*r = -EFAULT;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
delta = oneshot.timeout_abs_ns - get_kvmclock_ns(vcpu->kvm);
|
||||||
|
if ((oneshot.flags & VCPU_SSHOTTMR_future) && delta < 0) {
|
||||||
|
*r = -ETIME;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
kvm_xen_start_timer(vcpu, oneshot.timeout_abs_ns, delta);
|
||||||
|
*r = 0;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case VCPUOP_stop_singleshot_timer:
|
||||||
|
if (vcpu->arch.xen.vcpu_id != vcpu_id) {
|
||||||
|
*r = -EINVAL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
kvm_xen_stop_timer(vcpu);
|
||||||
|
*r = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool kvm_xen_hcall_set_timer_op(struct kvm_vcpu *vcpu, uint64_t timeout,
|
||||||
|
u64 *r)
|
||||||
|
{
|
||||||
|
if (!kvm_xen_timer_enabled(vcpu))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (timeout) {
|
||||||
|
uint64_t guest_now = get_kvmclock_ns(vcpu->kvm);
|
||||||
|
int64_t delta = timeout - guest_now;
|
||||||
|
|
||||||
|
/* Xen has a 'Linux workaround' in do_set_timer_op() which
|
||||||
|
* checks for negative absolute timeout values (caused by
|
||||||
|
* integer overflow), and for values about 13 days in the
|
||||||
|
* future (2^50ns) which would be caused by jiffies
|
||||||
|
* overflow. For those cases, it sets the timeout 100ms in
|
||||||
|
* the future (not *too* soon, since if a guest really did
|
||||||
|
* set a long timeout on purpose we don't want to keep
|
||||||
|
* churning CPU time by waking it up).
|
||||||
|
*/
|
||||||
|
if (unlikely((int64_t)timeout < 0 ||
|
||||||
|
(delta > 0 && (uint32_t) (delta >> 50) != 0))) {
|
||||||
|
delta = 100 * NSEC_PER_MSEC;
|
||||||
|
timeout = guest_now + delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
kvm_xen_start_timer(vcpu, timeout, delta);
|
||||||
|
} else {
|
||||||
|
kvm_xen_stop_timer(vcpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
*r = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
|
int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
bool longmode;
|
bool longmode;
|
||||||
@ -870,6 +1066,18 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
|
|||||||
case __HYPERVISOR_sched_op:
|
case __HYPERVISOR_sched_op:
|
||||||
handled = kvm_xen_hcall_sched_op(vcpu, params[0], params[1], &r);
|
handled = kvm_xen_hcall_sched_op(vcpu, params[0], params[1], &r);
|
||||||
break;
|
break;
|
||||||
|
case __HYPERVISOR_vcpu_op:
|
||||||
|
handled = kvm_xen_hcall_vcpu_op(vcpu, longmode, params[0], params[1],
|
||||||
|
params[2], &r);
|
||||||
|
break;
|
||||||
|
case __HYPERVISOR_set_timer_op: {
|
||||||
|
u64 timeout = params[0];
|
||||||
|
/* In 32-bit mode, the 64-bit timeout is in two 32-bit params. */
|
||||||
|
if (!longmode)
|
||||||
|
timeout |= params[1] << 32;
|
||||||
|
handled = kvm_xen_hcall_set_timer_op(vcpu, timeout, &r);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1398,6 +1606,9 @@ void kvm_xen_init_vcpu(struct kvm_vcpu *vcpu)
|
|||||||
|
|
||||||
void kvm_xen_destroy_vcpu(struct kvm_vcpu *vcpu)
|
void kvm_xen_destroy_vcpu(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
|
if (kvm_xen_timer_enabled(vcpu))
|
||||||
|
kvm_xen_stop_timer(vcpu);
|
||||||
|
|
||||||
kvm_gfn_to_pfn_cache_destroy(vcpu->kvm,
|
kvm_gfn_to_pfn_cache_destroy(vcpu->kvm,
|
||||||
&vcpu->arch.xen.runstate_cache);
|
&vcpu->arch.xen.runstate_cache);
|
||||||
kvm_gfn_to_pfn_cache_destroy(vcpu->kvm,
|
kvm_gfn_to_pfn_cache_destroy(vcpu->kvm,
|
||||||
|
@ -62,6 +62,20 @@ static inline bool kvm_xen_has_pending_events(struct kvm_vcpu *vcpu)
|
|||||||
vcpu->arch.xen.evtchn_pending_sel;
|
vcpu->arch.xen.evtchn_pending_sel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool kvm_xen_timer_enabled(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
return !!vcpu->arch.xen.timer_virq;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int kvm_xen_has_pending_timer(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
if (kvm_xen_hypercall_enabled(vcpu->kvm) && kvm_xen_timer_enabled(vcpu))
|
||||||
|
return atomic_read(&vcpu->arch.xen.timer_pending);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvm_xen_inject_timer_irqs(struct kvm_vcpu *vcpu);
|
||||||
#else
|
#else
|
||||||
static inline int kvm_xen_write_hypercall_page(struct kvm_vcpu *vcpu, u64 data)
|
static inline int kvm_xen_write_hypercall_page(struct kvm_vcpu *vcpu, u64 data)
|
||||||
{
|
{
|
||||||
@ -107,6 +121,20 @@ static inline bool kvm_xen_has_pending_events(struct kvm_vcpu *vcpu)
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int kvm_xen_has_pending_timer(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void kvm_xen_inject_timer_irqs(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool kvm_xen_timer_enabled(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int kvm_xen_hypercall(struct kvm_vcpu *vcpu);
|
int kvm_xen_hypercall(struct kvm_vcpu *vcpu);
|
||||||
|
@ -1748,6 +1748,11 @@ struct kvm_xen_vcpu_attr {
|
|||||||
__u64 time_offline;
|
__u64 time_offline;
|
||||||
} runstate;
|
} runstate;
|
||||||
__u32 vcpu_id;
|
__u32 vcpu_id;
|
||||||
|
struct {
|
||||||
|
__u32 port;
|
||||||
|
__u32 priority;
|
||||||
|
__u64 expires_ns;
|
||||||
|
} timer;
|
||||||
} u;
|
} u;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1760,6 +1765,7 @@ struct kvm_xen_vcpu_attr {
|
|||||||
#define KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADJUST 0x5
|
#define KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADJUST 0x5
|
||||||
/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_EVTCHN_SEND */
|
/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_EVTCHN_SEND */
|
||||||
#define KVM_XEN_VCPU_ATTR_TYPE_VCPU_ID 0x6
|
#define KVM_XEN_VCPU_ATTR_TYPE_VCPU_ID 0x6
|
||||||
|
#define KVM_XEN_VCPU_ATTR_TYPE_TIMER 0x7
|
||||||
|
|
||||||
/* Secure Encrypted Virtualization command */
|
/* Secure Encrypted Virtualization command */
|
||||||
enum sev_cmd_id {
|
enum sev_cmd_id {
|
||||||
|
Loading…
Reference in New Issue
Block a user