#include "hyperv.h"
#include <linux/kvm_host.h>
+#include <linux/sched/stat.h>
#include <trace/events/kvm.h>
#include <xen/interface/xen.h>
+#include <xen/interface/vcpu.h>
#include "trace.h"
return ret;
}
+static void kvm_xen_update_runstate(struct kvm_vcpu *v, int state, u64 steal_ns)
+{
+ struct kvm_vcpu_xen *vcpu_xen = &v->arch.xen;
+ struct vcpu_runstate_info runstate;
+ unsigned int offset = offsetof(struct compat_vcpu_runstate_info, state_entry_time);
+ u64 now, delta;
+
+ BUILD_BUG_ON(sizeof(struct compat_vcpu_runstate_info) != 0x2c);
+
+#ifdef CONFIG_X86_64
+ /*
+ * The only difference is alignment of uint64_t in 32-bit.
+ * So the first field 'state' is accessed via *runstate_state
+ * which is unmodified, while the other fields are accessed
+ * through 'runstate->' which we tweak here by adding 4.
+ */
+ BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, state_entry_time) !=
+ offsetof(struct compat_vcpu_runstate_info, state_entry_time) + 4);
+ BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, time) !=
+ offsetof(struct compat_vcpu_runstate_info, time) + 4);
+
+ offset = offsetof(struct vcpu_runstate_info, state_entry_time);
+#endif
+ /*
+ * Although it's called "state_entry_time" and explicitly documented
+ * as being "the system time at which the VCPU was last scheduled to
+ * run", Xen just treats it as a counter for HVM domains too.
+ */
+ if (kvm_read_guest_offset_cached(v->kvm, &v->arch.xen.runstate_cache,
+ &runstate.state_entry_time, offset,
+ sizeof(u64) * 5))
+ return;
+
+ runstate.state_entry_time = XEN_RUNSTATE_UPDATE |
+ (runstate.state_entry_time + 1);
+
+ if (kvm_write_guest_offset_cached(v->kvm, &v->arch.xen.runstate_cache,
+ &runstate.state_entry_time, offset,
+ sizeof(u64)))
+ return;
+ smp_wmb();
+
+ BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, state) !=
+ offsetof(struct compat_vcpu_runstate_info, state));
+ BUILD_BUG_ON(sizeof(((struct vcpu_runstate_info *)0)->state) !=
+ sizeof(((struct compat_vcpu_runstate_info *)0)->state));
+ if (kvm_write_guest_offset_cached(v->kvm, &v->arch.xen.runstate_cache,
+ &state,
+ offsetof(struct vcpu_runstate_info, state),
+ sizeof(runstate.state)))
+ return;
+
+ now = ktime_get_ns();
+ delta = now - vcpu_xen->last_state_ns - steal_ns;
+ runstate.time[vcpu_xen->current_runstate] += delta;
+ if (steal_ns)
+ runstate.time[RUNSTATE_runnable] += steal_ns;
+
+ BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, state_entry_time) !=
+ offsetof(struct vcpu_runstate_info, time) - sizeof(u64));
+ BUILD_BUG_ON(offsetof(struct compat_vcpu_runstate_info, state_entry_time) !=
+ offsetof(struct compat_vcpu_runstate_info, time) - sizeof(u64));
+ BUILD_BUG_ON(sizeof(((struct vcpu_runstate_info *)0)->time) !=
+ sizeof(((struct compat_vcpu_runstate_info *)0)->time));
+ if (kvm_write_guest_offset_cached(v->kvm, &v->arch.xen.runstate_cache,
+ &runstate.time[0],
+ offset + sizeof(u64),
+ sizeof(runstate.time)))
+ return;
+ smp_wmb();
+ vcpu_xen->current_runstate = state;
+ vcpu_xen->last_state_ns = now;
+
+ runstate.state_entry_time &= ~XEN_RUNSTATE_UPDATE;
+ if (kvm_write_guest_offset_cached(v->kvm, &v->arch.xen.runstate_cache,
+ &runstate.state_entry_time, offset,
+ sizeof(u64)))
+ return;
+}
+
+void kvm_xen_runstate_set_preempted(struct kvm_vcpu *v)
+{
+ struct kvm_vcpu_xen *vcpu_xen = &v->arch.xen;
+ int new_state;
+
+ BUILD_BUG_ON(sizeof(struct compat_vcpu_runstate_info) != 0x2c);
+ BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, state) !=
+ offsetof(struct compat_vcpu_runstate_info, state));
+ BUILD_BUG_ON(sizeof(((struct vcpu_runstate_info *)0)->state) !=
+ sizeof(((struct compat_vcpu_runstate_info *)0)->state));
+
+ if (v->preempted) {
+ new_state = RUNSTATE_runnable;
+ } else {
+ new_state = RUNSTATE_blocked;
+ vcpu_xen->last_steal = current->sched_info.run_delay;
+ }
+
+ kvm_xen_update_runstate(v, new_state, 0);
+}
+
+void kvm_xen_setup_runstate_page(struct kvm_vcpu *v)
+{
+ struct kvm_vcpu_xen *vcpu_xen = &v->arch.xen;
+ u64 steal_time = 0;
+
+ /*
+ * If the CPU was blocked when it last stopped, presumably
+ * it became unblocked at some point because it's being run
+ * again now. The scheduler run_delay is the runnable time,
+ * to be subtracted from the blocked time.
+ */
+ if (vcpu_xen->current_runstate == RUNSTATE_blocked)
+ steal_time = current->sched_info.run_delay - vcpu_xen->last_steal;
+
+ kvm_xen_update_runstate(v, RUNSTATE_running, steal_time);
+}
+
int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
{
u8 rc = 0;
}
break;
+ case KVM_XEN_VCPU_ATTR_TYPE_VCPU_RUNSTATE:
+ r = kvm_gfn_to_hva_cache_init(vcpu->kvm,
+ &vcpu->arch.xen.runstate_cache,
+ data->u.gpa,
+ sizeof(struct vcpu_runstate_info));
+ if (!r) {
+ vcpu->arch.xen.runstate_set = true;
+ vcpu->arch.xen.current_runstate = RUNSTATE_blocked;
+ vcpu->arch.xen.last_state_ns = ktime_get_ns();
+ }
+ break;
+
default:
break;
}
}
break;
+ case KVM_XEN_VCPU_ATTR_TYPE_VCPU_RUNSTATE:
+ if (vcpu->arch.xen.runstate_set) {
+ data->u.gpa = vcpu->arch.xen.runstate_cache.gpa;
+ r = 0;
+ }
+ break;
+
default:
break;
}