2019-08-30 08:45:27

by Steven Price

[permalink] [raw]
Subject: [PATCH v4 00/10] arm64: Stolen time support

This series add support for paravirtualized time for arm64 guests and
KVM hosts following the specification in Arm's document DEN 0057A:

https://developer.arm.com/docs/den0057/a

It implements support for stolen time, allowing the guest to
identify time when it is forcibly not executing.

It doesn't implement support for Live Physical Time (LPT) as there are
some concerns about the overheads and approach in the above
specification, and I expect an updated version of the specification to
be released soon with just the stolen time parts.

NOTE: Patches 8 and 9 will conflict with Mark Rutland's series[1] cleaning
up the SMCCC conduit. I do feel that the addition of an _invoke() call
makes a number of call sites cleaner and it should be possible to
integrate both this and Mark's other cleanups.

[1] https://lore.kernel.org/linux-arm-kernel/[email protected]/

Also available as a git tree:
git://linux-arm.org/linux-sp.git stolen_time/v4

Changes from v3:
https://lore.kernel.org/lkml/[email protected]/
* There's no longer a PV_TIME device, instead there are attributes on
the VCPU. This allows the stolen time structures to be places
arbitrarily by user space (subject to 64 byte alignment).
* Split documentation between information on the hypercalls and the
attributes on the VCPU
* Fixed the type of SMCCC functions to return long not int

Changes from v2:
https://lore.kernel.org/lkml/[email protected]/
* Switched from using gfn_to_hva_cache to a new macro kvm_put_guest()
that can provide the single-copy atomicity required (on arm64). This
macro is added in patch 4.
* Tidied up the locking for kvm_update_stolen_time().
pagefault_disable() was unnecessary and the caller didn't need to
take kvm->srcu as the function does it itself.
* Removed struct kvm_arch_pvtime from the arm implementation, replaced
instead with inline static functions which are empty for arm.
* Fixed a few checkpatch --strict warnings.

Changes from v1:
https://lore.kernel.org/lkml/[email protected]/
* Host kernel no longer allocates the stolen time structure, instead it
is allocated by user space. This means the save/restore functionality
can be removed.
* Refactored the code so arm has stub implementations and to avoid
initcall
* Rebased to pick up Documentation/{virt->virtual} change
* Bunch of typo fixes

Christoffer Dall (1):
KVM: arm/arm64: Factor out hypercall handling from PSCI code

Steven Price (9):
KVM: arm64: Document PV-time interface
KVM: arm64: Implement PV_FEATURES call
KVM: Implement kvm_put_guest()
KVM: arm64: Support stolen time reporting via shared structure
KVM: Allow kvm_device_ops to be const
KVM: arm64: Provide VCPU attributes for stolen time
arm/arm64: Provide a wrapper for SMCCC 1.1 calls
arm/arm64: Make use of the SMCCC 1.1 wrapper
arm64: Retrieve stolen time as paravirtualized guest

Documentation/virt/kvm/arm/pvtime.txt | 64 ++++++++++
Documentation/virt/kvm/devices/vcpu.txt | 14 +++
arch/arm/include/asm/kvm_host.h | 26 +++++
arch/arm/kvm/Makefile | 2 +-
arch/arm/kvm/handle_exit.c | 2 +-
arch/arm/mm/proc-v7-bugs.c | 13 +--
arch/arm64/include/asm/kvm_host.h | 30 ++++-
arch/arm64/include/asm/paravirt.h | 9 +-
arch/arm64/include/asm/pvclock-abi.h | 17 +++
arch/arm64/include/uapi/asm/kvm.h | 2 +
arch/arm64/kernel/cpu_errata.c | 80 +++++--------
arch/arm64/kernel/paravirt.c | 148 ++++++++++++++++++++++++
arch/arm64/kernel/time.c | 3 +
arch/arm64/kvm/Kconfig | 1 +
arch/arm64/kvm/Makefile | 2 +
arch/arm64/kvm/guest.c | 9 ++
arch/arm64/kvm/handle_exit.c | 4 +-
include/kvm/arm_hypercalls.h | 43 +++++++
include/kvm/arm_psci.h | 2 +-
include/linux/arm-smccc.h | 58 ++++++++++
include/linux/cpuhotplug.h | 1 +
include/linux/kvm_host.h | 26 ++++-
include/linux/kvm_types.h | 2 +
include/uapi/linux/kvm.h | 2 +
virt/kvm/arm/arm.c | 11 ++
virt/kvm/arm/hypercalls.c | 68 +++++++++++
virt/kvm/arm/psci.c | 84 +-------------
virt/kvm/arm/pvtime.c | 124 ++++++++++++++++++++
virt/kvm/kvm_main.c | 6 +-
29 files changed, 699 insertions(+), 154 deletions(-)
create mode 100644 Documentation/virt/kvm/arm/pvtime.txt
create mode 100644 arch/arm64/include/asm/pvclock-abi.h
create mode 100644 include/kvm/arm_hypercalls.h
create mode 100644 virt/kvm/arm/hypercalls.c
create mode 100644 virt/kvm/arm/pvtime.c

--
2.20.1


2019-08-30 08:45:32

by Steven Price

[permalink] [raw]
Subject: [PATCH v4 06/10] KVM: Allow kvm_device_ops to be const

Currently a kvm_device_ops structure cannot be const without triggering
compiler warnings. However the structure doesn't need to be written to
and, by marking it const, it can be read-only in memory. Add some more
const keywords to allow this.

Signed-off-by: Steven Price <[email protected]>
---
include/linux/kvm_host.h | 4 ++--
virt/kvm/kvm_main.c | 6 +++---
2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index bf0ae1825b9c..4e65f7566913 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -1260,7 +1260,7 @@ extern unsigned int halt_poll_ns_grow_start;
extern unsigned int halt_poll_ns_shrink;

struct kvm_device {
- struct kvm_device_ops *ops;
+ const struct kvm_device_ops *ops;
struct kvm *kvm;
void *private;
struct list_head vm_node;
@@ -1313,7 +1313,7 @@ struct kvm_device_ops {
void kvm_device_get(struct kvm_device *dev);
void kvm_device_put(struct kvm_device *dev);
struct kvm_device *kvm_device_from_filp(struct file *filp);
-int kvm_register_device_ops(struct kvm_device_ops *ops, u32 type);
+int kvm_register_device_ops(const struct kvm_device_ops *ops, u32 type);
void kvm_unregister_device_ops(u32 type);

extern struct kvm_device_ops kvm_mpic_ops;
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index c6a91b044d8d..75488ebb87c9 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -3046,14 +3046,14 @@ struct kvm_device *kvm_device_from_filp(struct file *filp)
return filp->private_data;
}

-static struct kvm_device_ops *kvm_device_ops_table[KVM_DEV_TYPE_MAX] = {
+static const struct kvm_device_ops *kvm_device_ops_table[KVM_DEV_TYPE_MAX] = {
#ifdef CONFIG_KVM_MPIC
[KVM_DEV_TYPE_FSL_MPIC_20] = &kvm_mpic_ops,
[KVM_DEV_TYPE_FSL_MPIC_42] = &kvm_mpic_ops,
#endif
};

-int kvm_register_device_ops(struct kvm_device_ops *ops, u32 type)
+int kvm_register_device_ops(const struct kvm_device_ops *ops, u32 type)
{
if (type >= ARRAY_SIZE(kvm_device_ops_table))
return -ENOSPC;
@@ -3074,7 +3074,7 @@ void kvm_unregister_device_ops(u32 type)
static int kvm_ioctl_create_device(struct kvm *kvm,
struct kvm_create_device *cd)
{
- struct kvm_device_ops *ops = NULL;
+ const struct kvm_device_ops *ops = NULL;
struct kvm_device *dev;
bool test = cd->flags & KVM_CREATE_DEVICE_TEST;
int type;
--
2.20.1

2019-08-30 08:45:34

by Steven Price

[permalink] [raw]
Subject: [PATCH v4 05/10] KVM: arm64: Support stolen time reporting via shared structure

Implement the service call for configuring a shared structure between a
VCPU and the hypervisor in which the hypervisor can write the time
stolen from the VCPU's execution time by other tasks on the host.

The hypervisor allocates memory which is placed at an IPA chosen by user
space. The hypervisor then updates the shared structure using
kvm_put_guest() to ensure single copy atomicity of the 64-bit value
reporting the stolen time in nanoseconds.

Whenever stolen time is enabled by the guest, the stolen time counter is
reset.

The stolen time itself is retrieved from the sched_info structure
maintained by the Linux scheduler code. We enable SCHEDSTATS when
selecting KVM Kconfig to ensure this value is meaningful.

Signed-off-by: Steven Price <[email protected]>
---
arch/arm/include/asm/kvm_host.h | 20 +++++++++++
arch/arm64/include/asm/kvm_host.h | 21 +++++++++++-
arch/arm64/kvm/Kconfig | 1 +
include/linux/kvm_types.h | 2 ++
virt/kvm/arm/arm.c | 11 ++++++
virt/kvm/arm/hypercalls.c | 3 ++
virt/kvm/arm/pvtime.c | 56 +++++++++++++++++++++++++++++++
7 files changed, 113 insertions(+), 1 deletion(-)

diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
index 5a0c3569ebde..5c401482d62d 100644
--- a/arch/arm/include/asm/kvm_host.h
+++ b/arch/arm/include/asm/kvm_host.h
@@ -39,6 +39,7 @@
KVM_ARCH_REQ_FLAGS(0, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
#define KVM_REQ_IRQ_PENDING KVM_ARCH_REQ(1)
#define KVM_REQ_VCPU_RESET KVM_ARCH_REQ(2)
+#define KVM_REQ_RECORD_STEAL KVM_ARCH_REQ(3)

DECLARE_STATIC_KEY_FALSE(userspace_irqchip_in_use);

@@ -329,6 +330,25 @@ static inline long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)
return SMCCC_RET_NOT_SUPPORTED;
}

+static inline long kvm_hypercall_stolen_time(struct kvm_vcpu *vcpu)
+{
+ return SMCCC_RET_NOT_SUPPORTED;
+}
+
+static inline int kvm_update_stolen_time(struct kvm_vcpu *vcpu, bool init)
+{
+ return -ENOTSUPP;
+}
+
+static inline void kvm_arm_pvtime_vcpu_init(struct kvm_vcpu_arch *vcpu_arch)
+{
+}
+
+static inline bool kvm_arm_is_pvtime_enabled(struct kvm_vcpu_arch *vcpu_arch)
+{
+ return false;
+}
+
void kvm_mmu_wp_memory_region(struct kvm *kvm, int slot);

struct kvm_vcpu *kvm_mpidr_to_vcpu(struct kvm *kvm, unsigned long mpidr);
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 93b46d9526d0..1697e63f6dd8 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -44,6 +44,7 @@
KVM_ARCH_REQ_FLAGS(0, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
#define KVM_REQ_IRQ_PENDING KVM_ARCH_REQ(1)
#define KVM_REQ_VCPU_RESET KVM_ARCH_REQ(2)
+#define KVM_REQ_RECORD_STEAL KVM_ARCH_REQ(3)

DECLARE_STATIC_KEY_FALSE(userspace_irqchip_in_use);

@@ -338,8 +339,14 @@ struct kvm_vcpu_arch {
/* True when deferrable sysregs are loaded on the physical CPU,
* see kvm_vcpu_load_sysregs and kvm_vcpu_put_sysregs. */
bool sysregs_loaded_on_cpu;
-};

+ /* Guest PV state */
+ struct {
+ u64 steal;
+ u64 last_steal;
+ gpa_t base;
+ } steal;
+};
/* Pointer to the vcpu's SVE FFR for sve_{save,load}_state() */
#define vcpu_sve_pffr(vcpu) ((void *)((char *)((vcpu)->arch.sve_state) + \
sve_ffr_offset((vcpu)->arch.sve_max_vl)))
@@ -479,6 +486,18 @@ int kvm_perf_init(void);
int kvm_perf_teardown(void);

long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu);
+long kvm_hypercall_stolen_time(struct kvm_vcpu *vcpu);
+int kvm_update_stolen_time(struct kvm_vcpu *vcpu, bool init);
+
+static inline void kvm_arm_pvtime_vcpu_init(struct kvm_vcpu_arch *vcpu_arch)
+{
+ vcpu_arch->steal.base = GPA_INVALID;
+}
+
+static inline bool kvm_arm_is_pvtime_enabled(struct kvm_vcpu_arch *vcpu_arch)
+{
+ return (vcpu_arch->steal.base != GPA_INVALID);
+}

void kvm_set_sei_esr(struct kvm_vcpu *vcpu, u64 syndrome);

diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig
index a67121d419a2..d8b88e40d223 100644
--- a/arch/arm64/kvm/Kconfig
+++ b/arch/arm64/kvm/Kconfig
@@ -39,6 +39,7 @@ config KVM
select IRQ_BYPASS_MANAGER
select HAVE_KVM_IRQ_BYPASS
select HAVE_KVM_VCPU_RUN_PID_CHANGE
+ select SCHEDSTATS
---help---
Support hosting virtualized guest machines.
We don't support KVM with 16K page tables yet, due to the multiple
diff --git a/include/linux/kvm_types.h b/include/linux/kvm_types.h
index bde5374ae021..1c88e69db3d9 100644
--- a/include/linux/kvm_types.h
+++ b/include/linux/kvm_types.h
@@ -35,6 +35,8 @@ typedef unsigned long gva_t;
typedef u64 gpa_t;
typedef u64 gfn_t;

+#define GPA_INVALID (~(gpa_t)0)
+
typedef unsigned long hva_t;
typedef u64 hpa_t;
typedef u64 hfn_t;
diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
index 35a069815baf..eaceb2d0f0c0 100644
--- a/virt/kvm/arm/arm.c
+++ b/virt/kvm/arm/arm.c
@@ -40,6 +40,10 @@
#include <asm/kvm_coproc.h>
#include <asm/sections.h>

+#include <kvm/arm_hypercalls.h>
+#include <kvm/arm_pmu.h>
+#include <kvm/arm_psci.h>
+
#ifdef REQUIRES_VIRT
__asm__(".arch_extension virt");
#endif
@@ -350,6 +354,8 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)

kvm_arm_reset_debug_ptr(vcpu);

+ kvm_arm_pvtime_vcpu_init(&vcpu->arch);
+
return kvm_vgic_vcpu_init(vcpu);
}

@@ -379,6 +385,8 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
kvm_vcpu_load_sysregs(vcpu);
kvm_arch_vcpu_load_fp(vcpu);
kvm_vcpu_pmu_restore_guest(vcpu);
+ if (kvm_arm_is_pvtime_enabled(&vcpu->arch))
+ kvm_make_request(KVM_REQ_RECORD_STEAL, vcpu);

if (single_task_running())
vcpu_clear_wfe_traps(vcpu);
@@ -644,6 +652,9 @@ static void check_vcpu_requests(struct kvm_vcpu *vcpu)
* that a VCPU sees new virtual interrupts.
*/
kvm_check_request(KVM_REQ_IRQ_PENDING, vcpu);
+
+ if (kvm_check_request(KVM_REQ_RECORD_STEAL, vcpu))
+ kvm_update_stolen_time(vcpu, false);
}
}

diff --git a/virt/kvm/arm/hypercalls.c b/virt/kvm/arm/hypercalls.c
index e2521e0d3978..3091a5d2e842 100644
--- a/virt/kvm/arm/hypercalls.c
+++ b/virt/kvm/arm/hypercalls.c
@@ -56,6 +56,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
case ARM_SMCCC_HV_PV_FEATURES:
val = kvm_hypercall_pv_features(vcpu);
break;
+ case ARM_SMCCC_HV_PV_TIME_ST:
+ val = kvm_hypercall_stolen_time(vcpu);
+ break;
default:
return kvm_psci_call(vcpu);
}
diff --git a/virt/kvm/arm/pvtime.c b/virt/kvm/arm/pvtime.c
index 7887a61651c6..d9d0dbc6994b 100644
--- a/virt/kvm/arm/pvtime.c
+++ b/virt/kvm/arm/pvtime.c
@@ -3,8 +3,45 @@

#include <linux/arm-smccc.h>

+#include <asm/pvclock-abi.h>
+
#include <kvm/arm_hypercalls.h>

+int kvm_update_stolen_time(struct kvm_vcpu *vcpu, bool init)
+{
+ struct kvm *kvm = vcpu->kvm;
+ u64 steal;
+ u64 steal_le;
+ u64 offset;
+ int idx;
+ u64 base = vcpu->arch.steal.base;
+
+ if (base == GPA_INVALID)
+ return -ENOTSUPP;
+
+ /* Let's do the local bookkeeping */
+ steal = vcpu->arch.steal.steal;
+ steal += current->sched_info.run_delay - vcpu->arch.steal.last_steal;
+ vcpu->arch.steal.last_steal = current->sched_info.run_delay;
+ vcpu->arch.steal.steal = steal;
+
+ steal_le = cpu_to_le64(steal);
+ idx = srcu_read_lock(&kvm->srcu);
+ if (init) {
+ struct pvclock_vcpu_stolen_time init_values = {
+ .revision = 0,
+ .attributes = 0
+ };
+ kvm_write_guest(kvm, base, &init_values,
+ sizeof(init_values));
+ }
+ offset = offsetof(struct pvclock_vcpu_stolen_time, stolen_time);
+ kvm_put_guest(kvm, base + offset, steal_le, u64);
+ srcu_read_unlock(&kvm->srcu, idx);
+
+ return 0;
+}
+
long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)
{
u32 feature = smccc_get_arg1(vcpu);
@@ -12,6 +49,7 @@ long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)

switch (feature) {
case ARM_SMCCC_HV_PV_FEATURES:
+ case ARM_SMCCC_HV_PV_TIME_ST:
val = SMCCC_RET_SUCCESS;
break;
}
@@ -19,3 +57,21 @@ long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)
return val;
}

+long kvm_hypercall_stolen_time(struct kvm_vcpu *vcpu)
+{
+ int err;
+
+ /*
+ * Start counting stolen time from the time the guest requests
+ * the feature enabled.
+ */
+ vcpu->arch.steal.steal = 0;
+ vcpu->arch.steal.last_steal = current->sched_info.run_delay;
+
+ err = kvm_update_stolen_time(vcpu, true);
+
+ if (err)
+ return SMCCC_RET_NOT_SUPPORTED;
+
+ return vcpu->arch.steal.base;
+}
--
2.20.1

2019-08-30 08:45:50

by Steven Price

[permalink] [raw]
Subject: [PATCH v4 02/10] KVM: arm/arm64: Factor out hypercall handling from PSCI code

From: Christoffer Dall <[email protected]>

We currently intertwine the KVM PSCI implementation with the general
dispatch of hypercall handling, which makes perfect sense because PSCI
is the only category of hypercalls we support.

However, as we are about to support additional hypercalls, factor out
this functionality into a separate hypercall handler file.

Signed-off-by: Christoffer Dall <[email protected]>
[[email protected]: rebased]
Signed-off-by: Steven Price <[email protected]>
---
arch/arm/kvm/Makefile | 2 +-
arch/arm/kvm/handle_exit.c | 2 +-
arch/arm64/kvm/Makefile | 1 +
arch/arm64/kvm/handle_exit.c | 4 +-
include/kvm/arm_hypercalls.h | 43 ++++++++++++++++++
include/kvm/arm_psci.h | 2 +-
virt/kvm/arm/hypercalls.c | 59 +++++++++++++++++++++++++
virt/kvm/arm/psci.c | 84 +-----------------------------------
8 files changed, 110 insertions(+), 87 deletions(-)
create mode 100644 include/kvm/arm_hypercalls.h
create mode 100644 virt/kvm/arm/hypercalls.c

diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile
index 531e59f5be9c..ef4d01088efc 100644
--- a/arch/arm/kvm/Makefile
+++ b/arch/arm/kvm/Makefile
@@ -23,7 +23,7 @@ obj-y += kvm-arm.o init.o interrupts.o
obj-y += handle_exit.o guest.o emulate.o reset.o
obj-y += coproc.o coproc_a15.o coproc_a7.o vgic-v3-coproc.o
obj-y += $(KVM)/arm/arm.o $(KVM)/arm/mmu.o $(KVM)/arm/mmio.o
-obj-y += $(KVM)/arm/psci.o $(KVM)/arm/perf.o
+obj-y += $(KVM)/arm/psci.o $(KVM)/arm/perf.o $(KVM)/arm/hypercalls.o
obj-y += $(KVM)/arm/aarch32.o

obj-y += $(KVM)/arm/vgic/vgic.o
diff --git a/arch/arm/kvm/handle_exit.c b/arch/arm/kvm/handle_exit.c
index 2a6a1394d26e..e58a89d2f13f 100644
--- a/arch/arm/kvm/handle_exit.c
+++ b/arch/arm/kvm/handle_exit.c
@@ -9,7 +9,7 @@
#include <asm/kvm_emulate.h>
#include <asm/kvm_coproc.h>
#include <asm/kvm_mmu.h>
-#include <kvm/arm_psci.h>
+#include <kvm/arm_hypercalls.h>
#include <trace/events/kvm.h>

#include "trace.h"
diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
index 3ac1a64d2fb9..73dce4d47d47 100644
--- a/arch/arm64/kvm/Makefile
+++ b/arch/arm64/kvm/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_KVM_ARM_HOST) += hyp/
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/eventfd.o $(KVM)/vfio.o
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/arm.o $(KVM)/arm/mmu.o $(KVM)/arm/mmio.o
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/psci.o $(KVM)/arm/perf.o
+kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/hypercalls.o

kvm-$(CONFIG_KVM_ARM_HOST) += inject_fault.o regmap.o va_layout.o
kvm-$(CONFIG_KVM_ARM_HOST) += hyp.o hyp-init.o handle_exit.o
diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c
index 706cca23f0d2..aacfc55de44c 100644
--- a/arch/arm64/kvm/handle_exit.c
+++ b/arch/arm64/kvm/handle_exit.c
@@ -11,8 +11,6 @@
#include <linux/kvm.h>
#include <linux/kvm_host.h>

-#include <kvm/arm_psci.h>
-
#include <asm/esr.h>
#include <asm/exception.h>
#include <asm/kvm_asm.h>
@@ -22,6 +20,8 @@
#include <asm/debug-monitors.h>
#include <asm/traps.h>

+#include <kvm/arm_hypercalls.h>
+
#define CREATE_TRACE_POINTS
#include "trace.h"

diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
new file mode 100644
index 000000000000..0e2509d27910
--- /dev/null
+++ b/include/kvm/arm_hypercalls.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2019 Arm Ltd. */
+
+#ifndef __KVM_ARM_HYPERCALLS_H
+#define __KVM_ARM_HYPERCALLS_H
+
+#include <asm/kvm_emulate.h>
+
+int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
+
+static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
+{
+ return vcpu_get_reg(vcpu, 0);
+}
+
+static inline unsigned long smccc_get_arg1(struct kvm_vcpu *vcpu)
+{
+ return vcpu_get_reg(vcpu, 1);
+}
+
+static inline unsigned long smccc_get_arg2(struct kvm_vcpu *vcpu)
+{
+ return vcpu_get_reg(vcpu, 2);
+}
+
+static inline unsigned long smccc_get_arg3(struct kvm_vcpu *vcpu)
+{
+ return vcpu_get_reg(vcpu, 3);
+}
+
+static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
+ unsigned long a0,
+ unsigned long a1,
+ unsigned long a2,
+ unsigned long a3)
+{
+ vcpu_set_reg(vcpu, 0, a0);
+ vcpu_set_reg(vcpu, 1, a1);
+ vcpu_set_reg(vcpu, 2, a2);
+ vcpu_set_reg(vcpu, 3, a3);
+}
+
+#endif
diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
index 632e78bdef4d..5b58bd2fe088 100644
--- a/include/kvm/arm_psci.h
+++ b/include/kvm/arm_psci.h
@@ -40,7 +40,7 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu, struct kvm *kvm)
}


-int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
+int kvm_psci_call(struct kvm_vcpu *vcpu);

struct kvm_one_reg;

diff --git a/virt/kvm/arm/hypercalls.c b/virt/kvm/arm/hypercalls.c
new file mode 100644
index 000000000000..f875241bd030
--- /dev/null
+++ b/virt/kvm/arm/hypercalls.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2019 Arm Ltd.
+
+#include <linux/arm-smccc.h>
+#include <linux/kvm_host.h>
+
+#include <asm/kvm_emulate.h>
+
+#include <kvm/arm_hypercalls.h>
+#include <kvm/arm_psci.h>
+
+int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
+{
+ u32 func_id = smccc_get_function(vcpu);
+ u32 val = SMCCC_RET_NOT_SUPPORTED;
+ u32 feature;
+
+ switch (func_id) {
+ case ARM_SMCCC_VERSION_FUNC_ID:
+ val = ARM_SMCCC_VERSION_1_1;
+ break;
+ case ARM_SMCCC_ARCH_FEATURES_FUNC_ID:
+ feature = smccc_get_arg1(vcpu);
+ switch (feature) {
+ case ARM_SMCCC_ARCH_WORKAROUND_1:
+ switch (kvm_arm_harden_branch_predictor()) {
+ case KVM_BP_HARDEN_UNKNOWN:
+ break;
+ case KVM_BP_HARDEN_WA_NEEDED:
+ val = SMCCC_RET_SUCCESS;
+ break;
+ case KVM_BP_HARDEN_NOT_REQUIRED:
+ val = SMCCC_RET_NOT_REQUIRED;
+ break;
+ }
+ break;
+ case ARM_SMCCC_ARCH_WORKAROUND_2:
+ switch (kvm_arm_have_ssbd()) {
+ case KVM_SSBD_FORCE_DISABLE:
+ case KVM_SSBD_UNKNOWN:
+ break;
+ case KVM_SSBD_KERNEL:
+ val = SMCCC_RET_SUCCESS;
+ break;
+ case KVM_SSBD_FORCE_ENABLE:
+ case KVM_SSBD_MITIGATED:
+ val = SMCCC_RET_NOT_REQUIRED;
+ break;
+ }
+ break;
+ }
+ break;
+ default:
+ return kvm_psci_call(vcpu);
+ }
+
+ smccc_set_retval(vcpu, val, 0, 0, 0);
+ return 1;
+}
diff --git a/virt/kvm/arm/psci.c b/virt/kvm/arm/psci.c
index 87927f7e1ee7..17e2bdd4b76f 100644
--- a/virt/kvm/arm/psci.c
+++ b/virt/kvm/arm/psci.c
@@ -15,6 +15,7 @@
#include <asm/kvm_host.h>

#include <kvm/arm_psci.h>
+#include <kvm/arm_hypercalls.h>

/*
* This is an implementation of the Power State Coordination Interface
@@ -23,38 +24,6 @@

#define AFFINITY_MASK(level) ~((0x1UL << ((level) * MPIDR_LEVEL_BITS)) - 1)

-static u32 smccc_get_function(struct kvm_vcpu *vcpu)
-{
- return vcpu_get_reg(vcpu, 0);
-}
-
-static unsigned long smccc_get_arg1(struct kvm_vcpu *vcpu)
-{
- return vcpu_get_reg(vcpu, 1);
-}
-
-static unsigned long smccc_get_arg2(struct kvm_vcpu *vcpu)
-{
- return vcpu_get_reg(vcpu, 2);
-}
-
-static unsigned long smccc_get_arg3(struct kvm_vcpu *vcpu)
-{
- return vcpu_get_reg(vcpu, 3);
-}
-
-static void smccc_set_retval(struct kvm_vcpu *vcpu,
- unsigned long a0,
- unsigned long a1,
- unsigned long a2,
- unsigned long a3)
-{
- vcpu_set_reg(vcpu, 0, a0);
- vcpu_set_reg(vcpu, 1, a1);
- vcpu_set_reg(vcpu, 2, a2);
- vcpu_set_reg(vcpu, 3, a3);
-}
-
static unsigned long psci_affinity_mask(unsigned long affinity_level)
{
if (affinity_level <= 3)
@@ -373,7 +342,7 @@ static int kvm_psci_0_1_call(struct kvm_vcpu *vcpu)
* Errors:
* -EINVAL: Unrecognized PSCI function
*/
-static int kvm_psci_call(struct kvm_vcpu *vcpu)
+int kvm_psci_call(struct kvm_vcpu *vcpu)
{
switch (kvm_psci_version(vcpu, vcpu->kvm)) {
case KVM_ARM_PSCI_1_0:
@@ -387,55 +356,6 @@ static int kvm_psci_call(struct kvm_vcpu *vcpu)
};
}

-int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
-{
- u32 func_id = smccc_get_function(vcpu);
- u32 val = SMCCC_RET_NOT_SUPPORTED;
- u32 feature;
-
- switch (func_id) {
- case ARM_SMCCC_VERSION_FUNC_ID:
- val = ARM_SMCCC_VERSION_1_1;
- break;
- case ARM_SMCCC_ARCH_FEATURES_FUNC_ID:
- feature = smccc_get_arg1(vcpu);
- switch(feature) {
- case ARM_SMCCC_ARCH_WORKAROUND_1:
- switch (kvm_arm_harden_branch_predictor()) {
- case KVM_BP_HARDEN_UNKNOWN:
- break;
- case KVM_BP_HARDEN_WA_NEEDED:
- val = SMCCC_RET_SUCCESS;
- break;
- case KVM_BP_HARDEN_NOT_REQUIRED:
- val = SMCCC_RET_NOT_REQUIRED;
- break;
- }
- break;
- case ARM_SMCCC_ARCH_WORKAROUND_2:
- switch (kvm_arm_have_ssbd()) {
- case KVM_SSBD_FORCE_DISABLE:
- case KVM_SSBD_UNKNOWN:
- break;
- case KVM_SSBD_KERNEL:
- val = SMCCC_RET_SUCCESS;
- break;
- case KVM_SSBD_FORCE_ENABLE:
- case KVM_SSBD_MITIGATED:
- val = SMCCC_RET_NOT_REQUIRED;
- break;
- }
- break;
- }
- break;
- default:
- return kvm_psci_call(vcpu);
- }
-
- smccc_set_retval(vcpu, val, 0, 0, 0);
- return 1;
-}
-
int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
{
return 3; /* PSCI version and two workaround registers */
--
2.20.1

2019-08-30 08:46:16

by Steven Price

[permalink] [raw]
Subject: [PATCH v4 08/10] arm/arm64: Provide a wrapper for SMCCC 1.1 calls

SMCCC 1.1 calls may use either HVC or SMC depending on the PSCI
conduit. Rather than coding this in every call site provide a macro
which uses the correct instruction. The macro also handles the case
where no PSCI conduit is configured returning a not supported error
in res, along with returning the conduit used for the call.

This allow us to remove some duplicated code and will be useful later
when adding paravirtualized time hypervisor calls.

Signed-off-by: Steven Price <[email protected]>
Acked-by: Will Deacon <[email protected]>
---
include/linux/arm-smccc.h | 44 +++++++++++++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)

diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
index e7f129f26ebd..eee1e832221d 100644
--- a/include/linux/arm-smccc.h
+++ b/include/linux/arm-smccc.h
@@ -303,6 +303,50 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1,
#define SMCCC_RET_NOT_SUPPORTED -1
#define SMCCC_RET_NOT_REQUIRED -2

+/* Like arm_smccc_1_1* but always returns SMCCC_RET_NOT_SUPPORTED.
+ * Used when the PSCI conduit is not defined. The empty asm statement
+ * avoids compiler warnings about unused variables.
+ */
+#define __fail_smccc_1_1(...) \
+ do { \
+ __declare_args(__count_args(__VA_ARGS__), __VA_ARGS__); \
+ asm ("" __constraints(__count_args(__VA_ARGS__))); \
+ if (___res) \
+ ___res->a0 = SMCCC_RET_NOT_SUPPORTED; \
+ } while (0)
+
+/*
+ * arm_smccc_1_1_invoke() - make an SMCCC v1.1 compliant call
+ *
+ * This is a variadic macro taking one to eight source arguments, and
+ * an optional return structure.
+ *
+ * @a0-a7: arguments passed in registers 0 to 7
+ * @res: result values from registers 0 to 3
+ *
+ * This macro will make either an HVC call or an SMC call depending on the
+ * current PSCI conduit. If no valid conduit is available then -1
+ * (SMCCC_RET_NOT_SUPPORTED) is returned in @res.a0 (if supplied).
+ *
+ * The return value also provides the conduit that was used.
+ */
+#define arm_smccc_1_1_invoke(...) ({ \
+ int method = psci_ops.conduit; \
+ switch (method) { \
+ case PSCI_CONDUIT_HVC: \
+ arm_smccc_1_1_hvc(__VA_ARGS__); \
+ break; \
+ case PSCI_CONDUIT_SMC: \
+ arm_smccc_1_1_smc(__VA_ARGS__); \
+ break; \
+ default: \
+ __fail_smccc_1_1(__VA_ARGS__); \
+ method = PSCI_CONDUIT_NONE; \
+ break; \
+ } \
+ method; \
+ })
+
/* Paravirtualised time calls (defined by ARM DEN0057A) */
#define ARM_SMCCC_HV_PV_FEATURES \
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
--
2.20.1

2019-08-30 09:44:04

by Christoffer Dall

[permalink] [raw]
Subject: Re: [PATCH v4 05/10] KVM: arm64: Support stolen time reporting via shared structure

On Fri, Aug 30, 2019 at 09:42:50AM +0100, Steven Price wrote:
> Implement the service call for configuring a shared structure between a
> VCPU and the hypervisor in which the hypervisor can write the time
> stolen from the VCPU's execution time by other tasks on the host.
>
> The hypervisor allocates memory which is placed at an IPA chosen by user
> space. The hypervisor then updates the shared structure using
> kvm_put_guest() to ensure single copy atomicity of the 64-bit value
> reporting the stolen time in nanoseconds.
>
> Whenever stolen time is enabled by the guest, the stolen time counter is
> reset.
>
> The stolen time itself is retrieved from the sched_info structure
> maintained by the Linux scheduler code. We enable SCHEDSTATS when
> selecting KVM Kconfig to ensure this value is meaningful.
>
> Signed-off-by: Steven Price <[email protected]>
> ---
> arch/arm/include/asm/kvm_host.h | 20 +++++++++++
> arch/arm64/include/asm/kvm_host.h | 21 +++++++++++-
> arch/arm64/kvm/Kconfig | 1 +
> include/linux/kvm_types.h | 2 ++
> virt/kvm/arm/arm.c | 11 ++++++
> virt/kvm/arm/hypercalls.c | 3 ++
> virt/kvm/arm/pvtime.c | 56 +++++++++++++++++++++++++++++++
> 7 files changed, 113 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
> index 5a0c3569ebde..5c401482d62d 100644
> --- a/arch/arm/include/asm/kvm_host.h
> +++ b/arch/arm/include/asm/kvm_host.h
> @@ -39,6 +39,7 @@
> KVM_ARCH_REQ_FLAGS(0, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
> #define KVM_REQ_IRQ_PENDING KVM_ARCH_REQ(1)
> #define KVM_REQ_VCPU_RESET KVM_ARCH_REQ(2)
> +#define KVM_REQ_RECORD_STEAL KVM_ARCH_REQ(3)
>
> DECLARE_STATIC_KEY_FALSE(userspace_irqchip_in_use);
>
> @@ -329,6 +330,25 @@ static inline long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)
> return SMCCC_RET_NOT_SUPPORTED;
> }
>
> +static inline long kvm_hypercall_stolen_time(struct kvm_vcpu *vcpu)
> +{
> + return SMCCC_RET_NOT_SUPPORTED;
> +}
> +
> +static inline int kvm_update_stolen_time(struct kvm_vcpu *vcpu, bool init)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline void kvm_arm_pvtime_vcpu_init(struct kvm_vcpu_arch *vcpu_arch)
> +{
> +}
> +
> +static inline bool kvm_arm_is_pvtime_enabled(struct kvm_vcpu_arch *vcpu_arch)
> +{
> + return false;
> +}
> +
> void kvm_mmu_wp_memory_region(struct kvm *kvm, int slot);
>
> struct kvm_vcpu *kvm_mpidr_to_vcpu(struct kvm *kvm, unsigned long mpidr);
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 93b46d9526d0..1697e63f6dd8 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -44,6 +44,7 @@
> KVM_ARCH_REQ_FLAGS(0, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
> #define KVM_REQ_IRQ_PENDING KVM_ARCH_REQ(1)
> #define KVM_REQ_VCPU_RESET KVM_ARCH_REQ(2)
> +#define KVM_REQ_RECORD_STEAL KVM_ARCH_REQ(3)
>
> DECLARE_STATIC_KEY_FALSE(userspace_irqchip_in_use);
>
> @@ -338,8 +339,14 @@ struct kvm_vcpu_arch {
> /* True when deferrable sysregs are loaded on the physical CPU,
> * see kvm_vcpu_load_sysregs and kvm_vcpu_put_sysregs. */
> bool sysregs_loaded_on_cpu;
> -};
>
> + /* Guest PV state */
> + struct {
> + u64 steal;
> + u64 last_steal;
> + gpa_t base;
> + } steal;
> +};
> /* Pointer to the vcpu's SVE FFR for sve_{save,load}_state() */
> #define vcpu_sve_pffr(vcpu) ((void *)((char *)((vcpu)->arch.sve_state) + \
> sve_ffr_offset((vcpu)->arch.sve_max_vl)))
> @@ -479,6 +486,18 @@ int kvm_perf_init(void);
> int kvm_perf_teardown(void);
>
> long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu);
> +long kvm_hypercall_stolen_time(struct kvm_vcpu *vcpu);
> +int kvm_update_stolen_time(struct kvm_vcpu *vcpu, bool init);
> +
> +static inline void kvm_arm_pvtime_vcpu_init(struct kvm_vcpu_arch *vcpu_arch)
> +{
> + vcpu_arch->steal.base = GPA_INVALID;
> +}
> +
> +static inline bool kvm_arm_is_pvtime_enabled(struct kvm_vcpu_arch *vcpu_arch)
> +{
> + return (vcpu_arch->steal.base != GPA_INVALID);
> +}
>
> void kvm_set_sei_esr(struct kvm_vcpu *vcpu, u64 syndrome);
>
> diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig
> index a67121d419a2..d8b88e40d223 100644
> --- a/arch/arm64/kvm/Kconfig
> +++ b/arch/arm64/kvm/Kconfig
> @@ -39,6 +39,7 @@ config KVM
> select IRQ_BYPASS_MANAGER
> select HAVE_KVM_IRQ_BYPASS
> select HAVE_KVM_VCPU_RUN_PID_CHANGE
> + select SCHEDSTATS
> ---help---
> Support hosting virtualized guest machines.
> We don't support KVM with 16K page tables yet, due to the multiple
> diff --git a/include/linux/kvm_types.h b/include/linux/kvm_types.h
> index bde5374ae021..1c88e69db3d9 100644
> --- a/include/linux/kvm_types.h
> +++ b/include/linux/kvm_types.h
> @@ -35,6 +35,8 @@ typedef unsigned long gva_t;
> typedef u64 gpa_t;
> typedef u64 gfn_t;
>
> +#define GPA_INVALID (~(gpa_t)0)
> +
> typedef unsigned long hva_t;
> typedef u64 hpa_t;
> typedef u64 hfn_t;
> diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
> index 35a069815baf..eaceb2d0f0c0 100644
> --- a/virt/kvm/arm/arm.c
> +++ b/virt/kvm/arm/arm.c
> @@ -40,6 +40,10 @@
> #include <asm/kvm_coproc.h>
> #include <asm/sections.h>
>
> +#include <kvm/arm_hypercalls.h>
> +#include <kvm/arm_pmu.h>
> +#include <kvm/arm_psci.h>
> +
> #ifdef REQUIRES_VIRT
> __asm__(".arch_extension virt");
> #endif
> @@ -350,6 +354,8 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
>
> kvm_arm_reset_debug_ptr(vcpu);
>
> + kvm_arm_pvtime_vcpu_init(&vcpu->arch);
> +
> return kvm_vgic_vcpu_init(vcpu);
> }
>
> @@ -379,6 +385,8 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
> kvm_vcpu_load_sysregs(vcpu);
> kvm_arch_vcpu_load_fp(vcpu);
> kvm_vcpu_pmu_restore_guest(vcpu);
> + if (kvm_arm_is_pvtime_enabled(&vcpu->arch))
> + kvm_make_request(KVM_REQ_RECORD_STEAL, vcpu);
>
> if (single_task_running())
> vcpu_clear_wfe_traps(vcpu);
> @@ -644,6 +652,9 @@ static void check_vcpu_requests(struct kvm_vcpu *vcpu)
> * that a VCPU sees new virtual interrupts.
> */
> kvm_check_request(KVM_REQ_IRQ_PENDING, vcpu);
> +
> + if (kvm_check_request(KVM_REQ_RECORD_STEAL, vcpu))
> + kvm_update_stolen_time(vcpu, false);
> }
> }
>
> diff --git a/virt/kvm/arm/hypercalls.c b/virt/kvm/arm/hypercalls.c
> index e2521e0d3978..3091a5d2e842 100644
> --- a/virt/kvm/arm/hypercalls.c
> +++ b/virt/kvm/arm/hypercalls.c
> @@ -56,6 +56,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> case ARM_SMCCC_HV_PV_FEATURES:
> val = kvm_hypercall_pv_features(vcpu);
> break;
> + case ARM_SMCCC_HV_PV_TIME_ST:
> + val = kvm_hypercall_stolen_time(vcpu);
> + break;
> default:
> return kvm_psci_call(vcpu);
> }
> diff --git a/virt/kvm/arm/pvtime.c b/virt/kvm/arm/pvtime.c
> index 7887a61651c6..d9d0dbc6994b 100644
> --- a/virt/kvm/arm/pvtime.c
> +++ b/virt/kvm/arm/pvtime.c
> @@ -3,8 +3,45 @@
>
> #include <linux/arm-smccc.h>
>
> +#include <asm/pvclock-abi.h>
> +
> #include <kvm/arm_hypercalls.h>
>
> +int kvm_update_stolen_time(struct kvm_vcpu *vcpu, bool init)
> +{
> + struct kvm *kvm = vcpu->kvm;
> + u64 steal;
> + u64 steal_le;
> + u64 offset;
> + int idx;
> + u64 base = vcpu->arch.steal.base;
> +
> + if (base == GPA_INVALID)
> + return -ENOTSUPP;
> +
> + /* Let's do the local bookkeeping */
> + steal = vcpu->arch.steal.steal;
> + steal += current->sched_info.run_delay - vcpu->arch.steal.last_steal;
> + vcpu->arch.steal.last_steal = current->sched_info.run_delay;
> + vcpu->arch.steal.steal = steal;
> +
> + steal_le = cpu_to_le64(steal);
> + idx = srcu_read_lock(&kvm->srcu);
> + if (init) {
> + struct pvclock_vcpu_stolen_time init_values = {
> + .revision = 0,
> + .attributes = 0
> + };
> + kvm_write_guest(kvm, base, &init_values,
> + sizeof(init_values));
> + }
> + offset = offsetof(struct pvclock_vcpu_stolen_time, stolen_time);
> + kvm_put_guest(kvm, base + offset, steal_le, u64);

Let's hope we don't have thousands of memslots through which we have to
do a linear scan on every vcpu load after this. If that were the case,
I think the memslot search path would have to be updated anyhow.

Otherwise looks reasonable to me.

Thanks,

Christoffer

> + srcu_read_unlock(&kvm->srcu, idx);
> +
> + return 0;
> +}
> +
> long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)
> {
> u32 feature = smccc_get_arg1(vcpu);
> @@ -12,6 +49,7 @@ long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)
>
> switch (feature) {
> case ARM_SMCCC_HV_PV_FEATURES:
> + case ARM_SMCCC_HV_PV_TIME_ST:
> val = SMCCC_RET_SUCCESS;
> break;
> }
> @@ -19,3 +57,21 @@ long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)
> return val;
> }
>
> +long kvm_hypercall_stolen_time(struct kvm_vcpu *vcpu)
> +{
> + int err;
> +
> + /*
> + * Start counting stolen time from the time the guest requests
> + * the feature enabled.
> + */
> + vcpu->arch.steal.steal = 0;
> + vcpu->arch.steal.last_steal = current->sched_info.run_delay;
> +
> + err = kvm_update_stolen_time(vcpu, true);
> +
> + if (err)
> + return SMCCC_RET_NOT_SUPPORTED;
> +
> + return vcpu->arch.steal.base;
> +}
> --
> 2.20.1
>
> _______________________________________________
> kvmarm mailing list
> [email protected]
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

2019-08-30 09:53:18

by Steven Price

[permalink] [raw]
Subject: Re: [PATCH v4 05/10] KVM: arm64: Support stolen time reporting via shared structure

On 30/08/2019 10:42, Christoffer Dall wrote:
> On Fri, Aug 30, 2019 at 09:42:50AM +0100, Steven Price wrote:
>> Implement the service call for configuring a shared structure between a
>> VCPU and the hypervisor in which the hypervisor can write the time
>> stolen from the VCPU's execution time by other tasks on the host.
>>
>> The hypervisor allocates memory which is placed at an IPA chosen by user
>> space. The hypervisor then updates the shared structure using
>> kvm_put_guest() to ensure single copy atomicity of the 64-bit value
>> reporting the stolen time in nanoseconds.
>>
>> Whenever stolen time is enabled by the guest, the stolen time counter is
>> reset.
>>
>> The stolen time itself is retrieved from the sched_info structure
>> maintained by the Linux scheduler code. We enable SCHEDSTATS when
>> selecting KVM Kconfig to ensure this value is meaningful.
>>
>> Signed-off-by: Steven Price <[email protected]>
[...]
>> +int kvm_update_stolen_time(struct kvm_vcpu *vcpu, bool init)
>> +{
>> + struct kvm *kvm = vcpu->kvm;
>> + u64 steal;
>> + u64 steal_le;
>> + u64 offset;
>> + int idx;
>> + u64 base = vcpu->arch.steal.base;
>> +
>> + if (base == GPA_INVALID)
>> + return -ENOTSUPP;
>> +
>> + /* Let's do the local bookkeeping */
>> + steal = vcpu->arch.steal.steal;
>> + steal += current->sched_info.run_delay - vcpu->arch.steal.last_steal;
>> + vcpu->arch.steal.last_steal = current->sched_info.run_delay;
>> + vcpu->arch.steal.steal = steal;
>> +
>> + steal_le = cpu_to_le64(steal);
>> + idx = srcu_read_lock(&kvm->srcu);
>> + if (init) {
>> + struct pvclock_vcpu_stolen_time init_values = {
>> + .revision = 0,
>> + .attributes = 0
>> + };
>> + kvm_write_guest(kvm, base, &init_values,
>> + sizeof(init_values));
>> + }
>> + offset = offsetof(struct pvclock_vcpu_stolen_time, stolen_time);
>> + kvm_put_guest(kvm, base + offset, steal_le, u64);
>
> Let's hope we don't have thousands of memslots through which we have to
> do a linear scan on every vcpu load after this. If that were the case,
> I think the memslot search path would have to be updated anyhow.

Yes I'm not sure with the current memslot implementation it is actually
beneficial to split up the stolen time structures into separate
memslots. But there's nothing requiring the use of so many memslots.

If this is really a problem it would be possible to implement a
memslot-caching kvm_put_guest(), but I'd want to wait until someone
shows there's actually a problem first.

> Otherwise looks reasonable to me.

Great, thanks for the review.

Steve

2019-09-02 07:24:09

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v4 02/10] KVM: arm/arm64: Factor out hypercall handling from PSCI code

Hi Steven,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linus/master]
[cannot apply to v5.3-rc6 next-20190830]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Steven-Price/arm64-Stolen-time-support/20190901-185152
config: i386-randconfig-a002-201935 (attached as .config)
compiler: gcc-7 (Debian 7.4.0-11) 7.4.0
reproduce:
# save the attached .config to linux build tree
make ARCH=i386

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <[email protected]>

All error/warnings (new ones prefixed by >>):

In file included from include/kvm/arm_hypercalls.h:7:0,
from <command-line>:0:
>> arch/x86/include/asm/kvm_emulate.h:349:22: error: 'NR_VCPU_REGS' undeclared here (not in a function)
unsigned long _regs[NR_VCPU_REGS];
^~~~~~~~~~~~
In file included from <command-line>:0:0:
>> include/kvm/arm_hypercalls.h:9:33: warning: 'struct kvm_vcpu' declared inside parameter list will not be visible outside of this definition or declaration
int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
^~~~~~~~
include/kvm/arm_hypercalls.h:11:45: warning: 'struct kvm_vcpu' declared inside parameter list will not be visible outside of this definition or declaration
static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
^~~~~~~~
include/kvm/arm_hypercalls.h: In function 'smccc_get_function':
>> include/kvm/arm_hypercalls.h:13:9: error: implicit declaration of function 'vcpu_get_reg' [-Werror=implicit-function-declaration]
return vcpu_get_reg(vcpu, 0);
^~~~~~~~~~~~
include/kvm/arm_hypercalls.h: At top level:
include/kvm/arm_hypercalls.h:16:51: warning: 'struct kvm_vcpu' declared inside parameter list will not be visible outside of this definition or declaration
static inline unsigned long smccc_get_arg1(struct kvm_vcpu *vcpu)
^~~~~~~~
include/kvm/arm_hypercalls.h:21:51: warning: 'struct kvm_vcpu' declared inside parameter list will not be visible outside of this definition or declaration
static inline unsigned long smccc_get_arg2(struct kvm_vcpu *vcpu)
^~~~~~~~
include/kvm/arm_hypercalls.h:26:51: warning: 'struct kvm_vcpu' declared inside parameter list will not be visible outside of this definition or declaration
static inline unsigned long smccc_get_arg3(struct kvm_vcpu *vcpu)
^~~~~~~~
include/kvm/arm_hypercalls.h:31:44: warning: 'struct kvm_vcpu' declared inside parameter list will not be visible outside of this definition or declaration
static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
^~~~~~~~
include/kvm/arm_hypercalls.h: In function 'smccc_set_retval':
>> include/kvm/arm_hypercalls.h:37:2: error: implicit declaration of function 'vcpu_set_reg'; did you mean 'smccc_set_retval'? [-Werror=implicit-function-declaration]
vcpu_set_reg(vcpu, 0, a0);
^~~~~~~~~~~~
smccc_set_retval
cc1: some warnings being treated as errors

vim +/NR_VCPU_REGS +349 arch/x86/include/asm/kvm_emulate.h

a584539b24b87d arch/x86/include/asm/kvm_emulate.h Paolo Bonzini 2015-04-01 290
9dac77fa4011bd arch/x86/include/asm/kvm_emulate.h Avi Kivity 2011-06-01 291 struct x86_emulate_ctxt {
0225fb509d51fc arch/x86/include/asm/kvm_emulate.h Mathias Krause 2012-08-30 292 const struct x86_emulate_ops *ops;
9dac77fa4011bd arch/x86/include/asm/kvm_emulate.h Avi Kivity 2011-06-01 293
9dac77fa4011bd arch/x86/include/asm/kvm_emulate.h Avi Kivity 2011-06-01 294 /* Register state before/after emulation. */
9dac77fa4011bd arch/x86/include/asm/kvm_emulate.h Avi Kivity 2011-06-01 295 unsigned long eflags;
9dac77fa4011bd arch/x86/include/asm/kvm_emulate.h Avi Kivity 2011-06-01 296 unsigned long eip; /* eip before instruction emulation */
9dac77fa4011bd arch/x86/include/asm/kvm_emulate.h Avi Kivity 2011-06-01 297 /* Emulated execution mode, represented by an X86EMUL_MODE value. */
9d1b39a967871b arch/x86/include/asm/kvm_emulate.h Gleb Natapov 2012-09-03 298 enum x86emul_mode mode;
9dac77fa4011bd arch/x86/include/asm/kvm_emulate.h Avi Kivity 2011-06-01 299
9dac77fa4011bd arch/x86/include/asm/kvm_emulate.h Avi Kivity 2011-06-01 300 /* interruptibility state, as a result of execution of STI or MOV SS */
9dac77fa4011bd arch/x86/include/asm/kvm_emulate.h Avi Kivity 2011-06-01 301 int interruptibility;
9dac77fa4011bd arch/x86/include/asm/kvm_emulate.h Avi Kivity 2011-06-01 302
9dac77fa4011bd arch/x86/include/asm/kvm_emulate.h Avi Kivity 2011-06-01 303 bool perm_ok; /* do not check permissions if true */
b51e974fcdabd0 arch/x86/include/asm/kvm_emulate.h Borislav Petkov 2013-09-22 304 bool ud; /* inject an #UD if host doesn't support insn */
c8401dda2f0a00 arch/x86/include/asm/kvm_emulate.h Paolo Bonzini 2017-06-07 305 bool tf; /* TF value before instruction (after for syscall/sysret) */
9dac77fa4011bd arch/x86/include/asm/kvm_emulate.h Avi Kivity 2011-06-01 306
9dac77fa4011bd arch/x86/include/asm/kvm_emulate.h Avi Kivity 2011-06-01 307 bool have_exception;
9dac77fa4011bd arch/x86/include/asm/kvm_emulate.h Avi Kivity 2011-06-01 308 struct x86_exception exception;
9dac77fa4011bd arch/x86/include/asm/kvm_emulate.h Avi Kivity 2011-06-01 309
1ce19dc16ce913 arch/x86/include/asm/kvm_emulate.h Borislav Petkov 2013-09-22 310 /*
1ce19dc16ce913 arch/x86/include/asm/kvm_emulate.h Borislav Petkov 2013-09-22 311 * decode cache
1ce19dc16ce913 arch/x86/include/asm/kvm_emulate.h Borislav Petkov 2013-09-22 312 */
1ce19dc16ce913 arch/x86/include/asm/kvm_emulate.h Borislav Petkov 2013-09-22 313
1ce19dc16ce913 arch/x86/include/asm/kvm_emulate.h Borislav Petkov 2013-09-22 314 /* current opcode length in bytes */
1ce19dc16ce913 arch/x86/include/asm/kvm_emulate.h Borislav Petkov 2013-09-22 315 u8 opcode_len;
e4e03deda83b1f drivers/kvm/x86_emulate.h Laurent Vivier 2007-09-18 316 u8 b;
c4f035c60dad45 arch/x86/include/asm/kvm_emulate.h Avi Kivity 2011-04-04 317 u8 intercept;
e4e03deda83b1f drivers/kvm/x86_emulate.h Laurent Vivier 2007-09-18 318 u8 op_bytes;
e4e03deda83b1f drivers/kvm/x86_emulate.h Laurent Vivier 2007-09-18 319 u8 ad_bytes;
e4e03deda83b1f drivers/kvm/x86_emulate.h Laurent Vivier 2007-09-18 320 struct operand src;
0dc8d10f7d848b arch/x86/include/asm/kvm_x86_emulate.h Guillaume Thouvenin 2008-12-04 321 struct operand src2;
e4e03deda83b1f drivers/kvm/x86_emulate.h Laurent Vivier 2007-09-18 322 struct operand dst;
ef65c88912cafe arch/x86/include/asm/kvm_emulate.h Avi Kivity 2010-07-29 323 int (*execute)(struct x86_emulate_ctxt *ctxt);
d09beabd7cd4cf arch/x86/include/asm/kvm_emulate.h Joerg Roedel 2011-04-04 324 int (*check_perm)(struct x86_emulate_ctxt *ctxt);
41061cdb98a0be arch/x86/include/asm/kvm_emulate.h Bandan Das 2014-04-16 325 /*
41061cdb98a0be arch/x86/include/asm/kvm_emulate.h Bandan Das 2014-04-16 326 * The following six fields are cleared together,
41061cdb98a0be arch/x86/include/asm/kvm_emulate.h Bandan Das 2014-04-16 327 * the rest are initialized unconditionally in x86_decode_insn
41061cdb98a0be arch/x86/include/asm/kvm_emulate.h Bandan Das 2014-04-16 328 * or elsewhere
41061cdb98a0be arch/x86/include/asm/kvm_emulate.h Bandan Das 2014-04-16 329 */
c44b4c6ab80eef arch/x86/include/asm/kvm_emulate.h Bandan Das 2014-04-16 330 bool rip_relative;
c44b4c6ab80eef arch/x86/include/asm/kvm_emulate.h Bandan Das 2014-04-16 331 u8 rex_prefix;
c44b4c6ab80eef arch/x86/include/asm/kvm_emulate.h Bandan Das 2014-04-16 332 u8 lock_prefix;
c44b4c6ab80eef arch/x86/include/asm/kvm_emulate.h Bandan Das 2014-04-16 333 u8 rep_prefix;
c44b4c6ab80eef arch/x86/include/asm/kvm_emulate.h Bandan Das 2014-04-16 334 /* bitmaps of registers in _regs[] that can be read */
c44b4c6ab80eef arch/x86/include/asm/kvm_emulate.h Bandan Das 2014-04-16 335 u32 regs_valid;
c44b4c6ab80eef arch/x86/include/asm/kvm_emulate.h Bandan Das 2014-04-16 336 /* bitmaps of registers in _regs[] that have been written */
c44b4c6ab80eef arch/x86/include/asm/kvm_emulate.h Bandan Das 2014-04-16 337 u32 regs_dirty;
e4e03deda83b1f drivers/kvm/x86_emulate.h Laurent Vivier 2007-09-18 338 /* modrm */
e4e03deda83b1f drivers/kvm/x86_emulate.h Laurent Vivier 2007-09-18 339 u8 modrm;
e4e03deda83b1f drivers/kvm/x86_emulate.h Laurent Vivier 2007-09-18 340 u8 modrm_mod;
e4e03deda83b1f drivers/kvm/x86_emulate.h Laurent Vivier 2007-09-18 341 u8 modrm_reg;
e4e03deda83b1f drivers/kvm/x86_emulate.h Laurent Vivier 2007-09-18 342 u8 modrm_rm;
09ee57cdae3156 arch/x86/include/asm/kvm_emulate.h Avi Kivity 2010-08-01 343 u8 modrm_seg;
573e80fe04db1a arch/x86/include/asm/kvm_emulate.h Bandan Das 2014-04-16 344 u8 seg_override;
c44b4c6ab80eef arch/x86/include/asm/kvm_emulate.h Bandan Das 2014-04-16 345 u64 d;
36dd9bb5ce32bc arch/x86/include/asm/kvm_emulate.h Avi Kivity 2011-06-01 346 unsigned long _eip;
cbd27ee783f1e5 arch/x86/include/asm/kvm_emulate.h Avi Kivity 2012-06-10 347 struct operand memop;
b5c9ff731f3cee arch/x86/include/asm/kvm_emulate.h Takuya Yoshikawa 2011-05-25 348 /* Fields above regs are cleared together. */
dd856efafe6097 arch/x86/include/asm/kvm_emulate.h Avi Kivity 2012-08-27 @349 unsigned long _regs[NR_VCPU_REGS];
f09ed83e211d25 arch/x86/include/asm/kvm_emulate.h Avi Kivity 2011-09-13 350 struct operand *memopp;
6226686954c4cc drivers/kvm/x86_emulate.h Avi Kivity 2007-11-20 351 struct fetch_cache fetch;
7b262e90fc20a4 arch/x86/include/asm/kvm_emulate.h Gleb Natapov 2010-03-18 352 struct read_cache io_read;
9de41573675cba arch/x86/include/asm/kvm_emulate.h Gleb Natapov 2010-04-28 353 struct read_cache mem_read;
e4e03deda83b1f drivers/kvm/x86_emulate.h Laurent Vivier 2007-09-18 354 };
e4e03deda83b1f drivers/kvm/x86_emulate.h Laurent Vivier 2007-09-18 355

:::::: The code at line 349 was first introduced by commit
:::::: dd856efafe6097a5c9104725c2bca74430423db8 KVM: x86 emulator: access GPRs on demand

:::::: TO: Avi Kivity <[email protected]>
:::::: CC: Marcelo Tosatti <[email protected]>

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (11.32 kB)
.config.gz (29.42 kB)
Download all attachments

2019-09-02 13:11:40

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v4 02/10] KVM: arm/arm64: Factor out hypercall handling from PSCI code

Hi Steven,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linus/master]
[cannot apply to v5.3-rc6 next-20190830]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Steven-Price/arm64-Stolen-time-support/20190901-185152
config: c6x-allyesconfig (attached as .config)
compiler: c6x-elf-gcc (GCC) 7.4.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=7.4.0 make.cross ARCH=c6x

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <[email protected]>

All errors (new ones prefixed by >>):

In file included from <command-line>:0:0:
>> include/kvm/arm_hypercalls.h:7:10: fatal error: asm/kvm_emulate.h: No such file or directory
#include <asm/kvm_emulate.h>
^~~~~~~~~~~~~~~~~~~
compilation terminated.

vim +7 include/kvm/arm_hypercalls.h

6
> 7 #include <asm/kvm_emulate.h>
8

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (1.31 kB)
.config.gz (48.55 kB)
Download all attachments

2019-09-03 08:05:09

by Andrew Jones

[permalink] [raw]
Subject: Re: [PATCH v4 00/10] arm64: Stolen time support

On Fri, Aug 30, 2019 at 09:42:45AM +0100, Steven Price wrote:
> This series add support for paravirtualized time for arm64 guests and
> KVM hosts following the specification in Arm's document DEN 0057A:
>
> https://developer.arm.com/docs/den0057/a
>
> It implements support for stolen time, allowing the guest to
> identify time when it is forcibly not executing.
>
> It doesn't implement support for Live Physical Time (LPT) as there are
> some concerns about the overheads and approach in the above
> specification, and I expect an updated version of the specification to
> be released soon with just the stolen time parts.
>
> NOTE: Patches 8 and 9 will conflict with Mark Rutland's series[1] cleaning
> up the SMCCC conduit. I do feel that the addition of an _invoke() call
> makes a number of call sites cleaner and it should be possible to
> integrate both this and Mark's other cleanups.
>
> [1] https://lore.kernel.org/linux-arm-kernel/[email protected]/
>
> Also available as a git tree:
> git://linux-arm.org/linux-sp.git stolen_time/v4
>
> Changes from v3:
> https://lore.kernel.org/lkml/[email protected]/
> * There's no longer a PV_TIME device, instead there are attributes on
> the VCPU. This allows the stolen time structures to be places
> arbitrarily by user space (subject to 64 byte alignment).
> * Split documentation between information on the hypercalls and the
> attributes on the VCPU
> * Fixed the type of SMCCC functions to return long not int
>
> Changes from v2:
> https://lore.kernel.org/lkml/[email protected]/
> * Switched from using gfn_to_hva_cache to a new macro kvm_put_guest()
> that can provide the single-copy atomicity required (on arm64). This
> macro is added in patch 4.
> * Tidied up the locking for kvm_update_stolen_time().
> pagefault_disable() was unnecessary and the caller didn't need to
> take kvm->srcu as the function does it itself.
> * Removed struct kvm_arch_pvtime from the arm implementation, replaced
> instead with inline static functions which are empty for arm.
> * Fixed a few checkpatch --strict warnings.
>
> Changes from v1:
> https://lore.kernel.org/lkml/[email protected]/
> * Host kernel no longer allocates the stolen time structure, instead it
> is allocated by user space. This means the save/restore functionality
> can be removed.
> * Refactored the code so arm has stub implementations and to avoid
> initcall
> * Rebased to pick up Documentation/{virt->virtual} change
> * Bunch of typo fixes
>
> Christoffer Dall (1):
> KVM: arm/arm64: Factor out hypercall handling from PSCI code
>
> Steven Price (9):
> KVM: arm64: Document PV-time interface
> KVM: arm64: Implement PV_FEATURES call
> KVM: Implement kvm_put_guest()
> KVM: arm64: Support stolen time reporting via shared structure
> KVM: Allow kvm_device_ops to be const
> KVM: arm64: Provide VCPU attributes for stolen time
> arm/arm64: Provide a wrapper for SMCCC 1.1 calls
> arm/arm64: Make use of the SMCCC 1.1 wrapper
> arm64: Retrieve stolen time as paravirtualized guest
>
> Documentation/virt/kvm/arm/pvtime.txt | 64 ++++++++++
> Documentation/virt/kvm/devices/vcpu.txt | 14 +++
> arch/arm/include/asm/kvm_host.h | 26 +++++
> arch/arm/kvm/Makefile | 2 +-
> arch/arm/kvm/handle_exit.c | 2 +-
> arch/arm/mm/proc-v7-bugs.c | 13 +--
> arch/arm64/include/asm/kvm_host.h | 30 ++++-
> arch/arm64/include/asm/paravirt.h | 9 +-
> arch/arm64/include/asm/pvclock-abi.h | 17 +++
> arch/arm64/include/uapi/asm/kvm.h | 2 +
> arch/arm64/kernel/cpu_errata.c | 80 +++++--------
> arch/arm64/kernel/paravirt.c | 148 ++++++++++++++++++++++++
> arch/arm64/kernel/time.c | 3 +
> arch/arm64/kvm/Kconfig | 1 +
> arch/arm64/kvm/Makefile | 2 +
> arch/arm64/kvm/guest.c | 9 ++
> arch/arm64/kvm/handle_exit.c | 4 +-
> include/kvm/arm_hypercalls.h | 43 +++++++
> include/kvm/arm_psci.h | 2 +-
> include/linux/arm-smccc.h | 58 ++++++++++
> include/linux/cpuhotplug.h | 1 +
> include/linux/kvm_host.h | 26 ++++-
> include/linux/kvm_types.h | 2 +
> include/uapi/linux/kvm.h | 2 +
> virt/kvm/arm/arm.c | 11 ++
> virt/kvm/arm/hypercalls.c | 68 +++++++++++
> virt/kvm/arm/psci.c | 84 +-------------
> virt/kvm/arm/pvtime.c | 124 ++++++++++++++++++++
> virt/kvm/kvm_main.c | 6 +-
> 29 files changed, 699 insertions(+), 154 deletions(-)
> create mode 100644 Documentation/virt/kvm/arm/pvtime.txt
> create mode 100644 arch/arm64/include/asm/pvclock-abi.h
> create mode 100644 include/kvm/arm_hypercalls.h
> create mode 100644 virt/kvm/arm/hypercalls.c
> create mode 100644 virt/kvm/arm/pvtime.c
>
> --
> 2.20.1
>

Hi Steven,

I had some fun testing this series with the KVM selftests framework. It
looks like it works to me, so you may add

Tested-by: Andrew Jones <[email protected]>

if you like. And below is the test I came up with.

Thanks,
drew


From: Andrew Jones <[email protected]>
Date: Tue, 3 Sep 2019 03:45:08 -0400
Subject: [PATCH] selftests: kvm: aarch64 stolen-time test

Signed-off-by: Andrew Jones <[email protected]>
---
tools/testing/selftests/kvm/Makefile | 1 +
.../selftests/kvm/aarch64/stolen-time.c | 208 ++++++++++++++++++
2 files changed, 209 insertions(+)
create mode 100644 tools/testing/selftests/kvm/aarch64/stolen-time.c

diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index ba7849751989..3151264039ad 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -28,6 +28,7 @@ TEST_GEN_PROGS_x86_64 += clear_dirty_log_test
TEST_GEN_PROGS_x86_64 += dirty_log_test
TEST_GEN_PROGS_x86_64 += kvm_create_max_vcpus

+TEST_GEN_PROGS_aarch64 += aarch64/stolen-time
TEST_GEN_PROGS_aarch64 += clear_dirty_log_test
TEST_GEN_PROGS_aarch64 += dirty_log_test
TEST_GEN_PROGS_aarch64 += kvm_create_max_vcpus
diff --git a/tools/testing/selftests/kvm/aarch64/stolen-time.c b/tools/testing/selftests/kvm/aarch64/stolen-time.c
new file mode 100644
index 000000000000..36df2f6baa17
--- /dev/null
+++ b/tools/testing/selftests/kvm/aarch64/stolen-time.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AArch64 PV stolen time test
+ *
+ * Copyright (C) 2019, Red Hat, Inc.
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <time.h>
+#include <sched.h>
+#include <pthread.h>
+#include <sys/syscall.h>
+#include "kvm_util.h"
+
+#define ST_IPA_BASE (1 << 30)
+#define MIN_STOLEN_TIME 200000
+
+struct st_time {
+ uint32_t rev;
+ uint32_t attr;
+ uint64_t st_time;
+};
+
+static uint64_t st_ipa_offset[4];
+static uint64_t guest_stolen_time[4];
+
+static void guest_code(void)
+{
+ struct st_time *st_time;
+ uint64_t cpu;
+ int64_t ipa;
+ int32_t ret;
+
+ asm volatile("mrs %0, mpidr_el1" : "=r" (cpu));
+ cpu &= 0x3;
+
+ asm volatile(
+ "mov x0, %1\n"
+ "mov x1, %2\n"
+ "hvc #0\n"
+ "mov %0, x0\n"
+ : "=r" (ret) : "r" (0x80000001), "r" (0xc5000020) :
+ "x0", "x1", "x2", "x3");
+
+ GUEST_ASSERT(ret == 0);
+
+ asm volatile(
+ "mov x0, %1\n"
+ "mov x1, %2\n"
+ "hvc #0\n"
+ "mov %0, x0\n"
+ : "=r" (ret) : "r" (0xc5000020), "r" (0xc5000022) :
+ "x0", "x1", "x2", "x3");
+
+ GUEST_ASSERT(ret == 0);
+
+ asm volatile(
+ "mov x0, %1\n"
+ "hvc #0\n"
+ "mov %0, x0\n"
+ : "=r" (ipa) : "r" (0xc5000022) :
+ "x0", "x1", "x2", "x3");
+
+ GUEST_ASSERT(ipa == ST_IPA_BASE + st_ipa_offset[cpu]);
+
+ st_time = (struct st_time *)ipa;
+ GUEST_ASSERT(st_time->rev == 0);
+ GUEST_ASSERT(st_time->attr == 0);
+
+ guest_stolen_time[cpu] = st_time->st_time;
+ GUEST_SYNC(0);
+
+ guest_stolen_time[cpu] = st_time->st_time;
+ GUEST_DONE();
+}
+
+static long get_run_delay(void)
+{
+ char path[64];
+ long val[2];
+ FILE *fp;
+
+ sprintf(path, "/proc/%ld/schedstat", syscall(SYS_gettid));
+ fp = fopen(path, "r");
+ fscanf(fp, "%ld %ld ", &val[0], &val[1]);
+ fclose(fp);
+
+ return val[1];
+}
+
+static void *steal_time(void *arg)
+{
+ uint64_t nsecs_per_sec = 1000000000ul;
+ uint64_t sec, nsec;
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ sec = ts.tv_sec;
+ nsec = ts.tv_nsec + MIN_STOLEN_TIME;
+ if (nsec > nsecs_per_sec) {
+ sec += 1;
+ nsec -= nsecs_per_sec;
+ }
+
+ while (1) {
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ if (ts.tv_sec > sec || (ts.tv_sec == sec && ts.tv_nsec >= nsec))
+ break;
+ }
+
+ return NULL;
+}
+
+static void run_vcpu(struct kvm_vm *vm, uint32_t vcpuid)
+{
+ struct ucall uc;
+
+ vcpu_ioctl(vm, vcpuid, KVM_RUN, NULL);
+
+ switch (get_ucall(vm, vcpuid, &uc)) {
+ case UCALL_SYNC:
+ case UCALL_DONE:
+ break;
+ case UCALL_ABORT:
+ TEST_ASSERT(false, "%s at %s:%d", (const char *)uc.args[0],
+ __FILE__, uc.args[1]);
+ default:
+ TEST_ASSERT(false, "Unexpected exit: %s",
+ exit_reason_str(vcpu_state(vm, vcpuid)->exit_reason));
+ }
+}
+
+int main(int ac, char **av)
+{
+ struct kvm_device_attr dev = {
+ .group = KVM_ARM_VCPU_PVTIME_CTRL,
+ .attr = KVM_ARM_VCPU_PVTIME_SET_IPA,
+ };
+ uint64_t pvtime_memslot_size;
+ struct kvm_vm *vm;
+ pthread_attr_t attr;
+ pthread_t thread;
+ cpu_set_t cpuset;
+ long stolen_time;
+ int i;
+
+ CPU_ZERO(&cpuset);
+ CPU_SET(0, &cpuset);
+ pthread_attr_init(&attr);
+ pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
+ pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
+
+ pvtime_memslot_size = 64 * 1024; /* one maximum-sized host page */
+
+ /* create a one-vcpu guest and the pvtime memslot */
+ vm = vm_create_default(0, 0, guest_code);
+ vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, ST_IPA_BASE, 1,
+ 16 /* vm uses 4k pages */, 0);
+ virt_map(vm, ST_IPA_BASE, ST_IPA_BASE, pvtime_memslot_size, 0);
+ ucall_init(vm, UCALL_MMIO, NULL);
+
+ /* add 3 more vcpus */
+ for (i = 1; i < 4; ++i) {
+ vm_vcpu_add_default(vm, i, guest_code);
+ st_ipa_offset[i] = i * 64;
+ sync_global_to_guest(vm, st_ipa_offset[i]);
+ }
+
+ /* add pvtime to each vcpu */
+ for (i = 0; i < 4; ++i) {
+ uint64_t st_ipa = ST_IPA_BASE + st_ipa_offset[i];
+ dev.addr = (uint64_t)&st_ipa;
+ vcpu_ioctl(vm, i, KVM_HAS_DEVICE_ATTR, &dev);
+ vcpu_ioctl(vm, i, KVM_SET_DEVICE_ATTR, &dev);
+ }
+
+ /* run the tests on each vcpu */
+ for (i = 0; i < 4; ++i) {
+ /* first vcpu run */
+ run_vcpu(vm, i);
+ sync_global_from_guest(vm, guest_stolen_time[i]);
+ TEST_ASSERT(guest_stolen_time[i] == 0, "Expected stolen_time = 0");
+
+ /* steal time from the vcpu */
+ stolen_time = get_run_delay();
+ pthread_create(&thread, &attr, steal_time, NULL);
+ pthread_yield();
+ pthread_join(thread, NULL);
+ stolen_time = get_run_delay() - stolen_time;
+ TEST_ASSERT(stolen_time >= MIN_STOLEN_TIME,
+ "Expected stolen time >= %ld, got %ld",
+ MIN_STOLEN_TIME, stolen_time);
+
+ /* run vcpu again and check the stolen time */
+ run_vcpu(vm, i);
+ sync_global_from_guest(vm, guest_stolen_time[i]);
+ TEST_ASSERT(guest_stolen_time[i] >= stolen_time,
+ "Expected stolen_time >= %ld, got %ld",
+ stolen_time, guest_stolen_time[i]);
+
+ printf("CPU%d: %ld", i, guest_stolen_time[i]);
+ if (stolen_time == guest_stolen_time[i])
+ printf(" (BONUS: guest stolen_time even exactly matches run_delay)");
+ printf("\n");
+ }
+
+ return 0;
+}
--
2.18.1

2019-09-03 08:50:33

by Andrew Jones

[permalink] [raw]
Subject: Re: [PATCH v4 00/10] arm64: Stolen time support

On Tue, Sep 03, 2019 at 10:03:48AM +0200, Andrew Jones wrote:
> Hi Steven,
>
> I had some fun testing this series with the KVM selftests framework. It
> looks like it works to me, so you may add
>
> Tested-by: Andrew Jones <[email protected]>
>

Actually, I probably shouldn't be quite so generous with this tag yet,
because I haven't yet tested the guest-side changes. To do that I'll
need to start prototyping something for QEMU. I need to finish some other
stuff first, but then I can do that.

Thanks,
drew

2019-09-03 09:21:18

by Zenghui Yu

[permalink] [raw]
Subject: Re: [PATCH v4 05/10] KVM: arm64: Support stolen time reporting via shared structure

On 2019/8/30 16:42, Steven Price wrote:
> Implement the service call for configuring a shared structure between a
> VCPU and the hypervisor in which the hypervisor can write the time
> stolen from the VCPU's execution time by other tasks on the host.
>
> The hypervisor allocates memory which is placed at an IPA chosen by user
> space.

It seems that no allocation happens in the hypervisor code. User space
will do it instead?

> The hypervisor then updates the shared structure using
> kvm_put_guest() to ensure single copy atomicity of the 64-bit value
> reporting the stolen time in nanoseconds.
>
> Whenever stolen time is enabled by the guest, the stolen time counter is
> reset.
>
> The stolen time itself is retrieved from the sched_info structure
> maintained by the Linux scheduler code. We enable SCHEDSTATS when
> selecting KVM Kconfig to ensure this value is meaningful.
>
> Signed-off-by: Steven Price <[email protected]>

Thanks,
zenghui

2019-09-04 15:06:46

by Steven Price

[permalink] [raw]
Subject: Re: [PATCH v4 02/10] KVM: arm/arm64: Factor out hypercall handling from PSCI code

On 02/09/2019 08:06, kbuild test robot wrote:
> Hi Steven,
>
> Thank you for the patch! Yet something to improve:
>
> [auto build test ERROR on linus/master]
> [cannot apply to v5.3-rc6 next-20190830]
> [if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
>
> url: https://github.com/0day-ci/linux/commits/Steven-Price/arm64-Stolen-time-support/20190901-185152
> config: i386-randconfig-a002-201935 (attached as .config)
> compiler: gcc-7 (Debian 7.4.0-11) 7.4.0
> reproduce:
> # save the attached .config to linux build tree
> make ARCH=i386
>
> If you fix the issue, kindly add following tag
> Reported-by: kbuild test robot <[email protected]>
>
> All error/warnings (new ones prefixed by >>):
>
> In file included from include/kvm/arm_hypercalls.h:7:0,
> from <command-line>:0:
>>> arch/x86/include/asm/kvm_emulate.h:349:22: error: 'NR_VCPU_REGS' undeclared here (not in a function)
> unsigned long _regs[NR_VCPU_REGS];
> ^~~~~~~~~~~~

This is because x86's asm/kvm_emulate.h can't be included by itself (and
doesn't even exist on other architectures). This new header file doesn't
make sense to include on x86, so I'll squash in the following to prevent
building the header file except on arm/arm64.

Steve

----8<----
diff --git a/include/Kbuild b/include/Kbuild
index c38f0d46b267..f775ea28716e 100644
--- a/include/Kbuild
+++ b/include/Kbuild
@@ -67,6 +67,8 @@ header-test- += keys/big_key-type.h
header-test- += keys/request_key_auth-type.h
header-test- += keys/trusted.h
header-test- += kvm/arm_arch_timer.h
+header-test-$(CONFIG_ARM) += kvm/arm_hypercalls.h
+header-test-$(CONFIG_ARM64) += kvm/arm_hypercalls.h
header-test- += kvm/arm_pmu.h
header-test-$(CONFIG_ARM) += kvm/arm_psci.h
header-test-$(CONFIG_ARM64) += kvm/arm_psci.h

2019-09-04 15:54:16

by Steven Price

[permalink] [raw]
Subject: Re: [PATCH v4 05/10] KVM: arm64: Support stolen time reporting via shared structure

On 03/09/2019 10:14, Zenghui Yu wrote:
> On 2019/8/30 16:42, Steven Price wrote:
>> Implement the service call for configuring a shared structure between a
>> VCPU and the hypervisor in which the hypervisor can write the time
>> stolen from the VCPU's execution time by other tasks on the host.
>>
>> The hypervisor allocates memory which is placed at an IPA chosen by user
>> space.
>
> It seems that no allocation happens in the hypervisor code.  User space
> will do it instead?

Ah, yes I should update the commit message. User space does now allocate
the memory. Thanks for spotting that.

Steve

>> The hypervisor then updates the shared structure using
>> kvm_put_guest() to ensure single copy atomicity of the 64-bit value
>> reporting the stolen time in nanoseconds.
>>
>> Whenever stolen time is enabled by the guest, the stolen time counter is
>> reset.
>>
>> The stolen time itself is retrieved from the sched_info structure
>> maintained by the Linux scheduler code. We enable SCHEDSTATS when
>> selecting KVM Kconfig to ensure this value is meaningful.
>>
>> Signed-off-by: Steven Price <[email protected]>
>
> Thanks,
> zenghui
>

2019-09-04 16:08:09

by Steven Price

[permalink] [raw]
Subject: Re: [PATCH v4 00/10] arm64: Stolen time support

On 03/09/2019 09:49, Andrew Jones wrote:
> On Tue, Sep 03, 2019 at 10:03:48AM +0200, Andrew Jones wrote:
>> Hi Steven,
>>
>> I had some fun testing this series with the KVM selftests framework. It
>> looks like it works to me, so you may add
>>
>> Tested-by: Andrew Jones <[email protected]>
>>
>
> Actually, I probably shouldn't be quite so generous with this tag yet,
> because I haven't yet tested the guest-side changes. To do that I'll
> need to start prototyping something for QEMU. I need to finish some other
> stuff first, but then I can do that.

Thanks for the testing, I'll wait for your other testing before adding
your Tested-by tag.

Steve