Extend the PV_TIME device to allow setting up Live Physical Time and saving/restoring the state necessary for migration. Signed-off-by: Steven Price <steven.price@xxxxxxx> --- arch/arm64/include/uapi/asm/kvm.h | 2 ++ virt/kvm/arm/pvtime.c | 55 +++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h index e1d42ff35430..d3b0d70cdd9f 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -310,8 +310,10 @@ struct kvm_vcpu_events { /* Device Control API: PV_TIME */ #define KVM_DEV_ARM_PV_TIME_PADDR 0 #define KVM_DEV_ARM_PV_TIME_ST 0 +#define KVM_DEV_ARM_PV_TIME_LPT 1 #define KVM_DEV_ARM_PV_TIME_STATE_SIZE 1 #define KVM_DEV_ARM_PV_TIME_STATE 2 +#define KVM_DEV_ARM_PV_TIME_FREQUENCY 3 #endif diff --git a/virt/kvm/arm/pvtime.c b/virt/kvm/arm/pvtime.c index e033f55e3ee4..345798aebebf 100644 --- a/virt/kvm/arm/pvtime.c +++ b/virt/kvm/arm/pvtime.c @@ -25,6 +25,14 @@ static int kvm_arm_pvtime_create(struct kvm_device *dev, u32 type) if (!pvtime->st) return -ENOMEM; + pvtime->lpt = (void *)get_zeroed_page(GFP_KERNEL); + if (!pvtime->lpt) { + free_pages_exact(pvtime->st, max_stolen_size()); + return -ENOMEM; + } + + pvtime->lpt_fpv = arch_timer_get_rate(); + return 0; } @@ -32,8 +40,10 @@ static void kvm_arm_pvtime_destroy(struct kvm_device *dev) { struct kvm_arch_pvtime *pvtime = &dev->kvm->arch.pvtime; + pvtime->lpt_base = GPA_INVALID; pvtime->st_base = GPA_INVALID; free_pages_exact(pvtime->st, max_stolen_size()); + free_page((unsigned long)pvtime->lpt); } static int pvtime_map_pages(struct kvm *kvm, gpa_t guest_paddr, @@ -50,6 +60,10 @@ static int pvtime_save_state(struct kvm *kvm, u64 type, void __user *user) size_t size; switch (type) { + case KVM_DEV_ARM_PV_TIME_LPT: + source = kvm->arch.pvtime.lpt; + size = sizeof(struct pvclock_vm_time_info); + break; case KVM_DEV_ARM_PV_TIME_ST: source = kvm->arch.pvtime.st; size = sizeof(struct pvclock_vcpu_stolen_time_info) * @@ -70,6 +84,10 @@ static int pvtime_restore_state(struct kvm *kvm, u64 type, void __user *user) size_t size; switch (type) { + case KVM_DEV_ARM_PV_TIME_LPT: + dest = kvm->arch.pvtime.lpt; + size = sizeof(struct pvclock_vm_time_info); + break; case KVM_DEV_ARM_PV_TIME_ST: dest = kvm->arch.pvtime.st; size = sizeof(struct pvclock_vcpu_stolen_time_info) * @@ -91,6 +109,7 @@ static int kvm_arm_pvtime_set_attr(struct kvm_device *dev, struct kvm_arch_pvtime *pvtime = &dev->kvm->arch.pvtime; u64 __user *user = (u64 __user *)attr->addr; u64 paddr; + u32 frequency; int ret; switch (attr->group) { @@ -100,6 +119,15 @@ static int kvm_arm_pvtime_set_attr(struct kvm_device *dev, if (paddr & 63) return -EINVAL; switch (attr->attr) { + case KVM_DEV_ARM_PV_TIME_LPT: + if (pvtime->lpt_base != GPA_INVALID) + return -EEXIST; + ret = pvtime_map_pages(dev->kvm, paddr, pvtime->lpt, + PAGE_SIZE); + if (ret) + return ret; + pvtime->lpt_base = paddr; + return kvm_arm_update_lpt_sequence(dev->kvm); case KVM_DEV_ARM_PV_TIME_ST: if (pvtime->st_base != GPA_INVALID) return -EEXIST; @@ -111,6 +139,13 @@ static int kvm_arm_pvtime_set_attr(struct kvm_device *dev, return 0; } break; + case KVM_DEV_ARM_PV_TIME_FREQUENCY: + if (attr->attr != KVM_DEV_ARM_PV_TIME_LPT) + break; + if (get_user(frequency, user)) + return -EFAULT; + pvtime->lpt_fpv = frequency; + return kvm_arm_update_lpt_sequence(dev->kvm); case KVM_DEV_ARM_PV_TIME_STATE_SIZE: return -EPERM; case KVM_DEV_ARM_PV_TIME_STATE: @@ -128,14 +163,27 @@ static int kvm_arm_pvtime_get_attr(struct kvm_device *dev, switch (attr->group) { case KVM_DEV_ARM_PV_TIME_PADDR: switch (attr->attr) { + case KVM_DEV_ARM_PV_TIME_LPT: + if (put_user(dev->kvm->arch.pvtime.lpt_base, user)) + return -EFAULT; + return 0; case KVM_DEV_ARM_PV_TIME_ST: if (put_user(dev->kvm->arch.pvtime.st_base, user)) return -EFAULT; return 0; } break; + case KVM_DEV_ARM_PV_TIME_FREQUENCY: + if (attr->attr != KVM_DEV_ARM_PV_TIME_LPT) + break; + if (put_user(dev->kvm->arch.pvtime.lpt_fpv, user)) + return -EFAULT; + return 0; case KVM_DEV_ARM_PV_TIME_STATE_SIZE: switch (attr->attr) { + case KVM_DEV_ARM_PV_TIME_LPT: + size = sizeof(struct pvclock_vm_time_info); + break; case KVM_DEV_ARM_PV_TIME_ST: size = sizeof(struct pvclock_vcpu_stolen_time_info); size *= atomic_read(&dev->kvm->online_vcpus); @@ -160,10 +208,17 @@ static int kvm_arm_pvtime_has_attr(struct kvm_device *dev, case KVM_DEV_ARM_PV_TIME_STATE_SIZE: case KVM_DEV_ARM_PV_TIME_STATE: switch (attr->attr) { + case KVM_DEV_ARM_PV_TIME_LPT: case KVM_DEV_ARM_PV_TIME_ST: return 0; } break; + case KVM_DEV_ARM_PV_TIME_FREQUENCY: + switch (attr->attr) { + case KVM_DEV_ARM_PV_TIME_LPT: + return 0; + } + break; } return -ENXIO; } -- 2.19.2 _______________________________________________ kvmarm mailing list kvmarm@xxxxxxxxxxxxxxxxxxxxx https://lists.cs.columbia.edu/mailman/listinfo/kvmarm