Skip to content

Commit e0685fa

Browse files
Steven PriceMarc Zyngier
authored andcommitted
arm64: Retrieve stolen time as paravirtualized guest
Enable paravirtualization features when running under a hypervisor supporting the PV_TIME_ST hypercall. For each (v)CPU, we ask the hypervisor for the location of a shared page which the hypervisor will use to report stolen time to us. We set pv_time_ops to the stolen time function which simply reads the stolen value from the shared page for a VCPU. We guarantee single-copy atomicity using READ_ONCE which means we can also read the stolen time for another VCPU than the currently running one while it is potentially being updated by the hypervisor. Signed-off-by: Steven Price <[email protected]> Signed-off-by: Marc Zyngier <[email protected]>
1 parent ce4d5ca commit e0685fa

File tree

5 files changed

+155
-4
lines changed

5 files changed

+155
-4
lines changed

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3083,9 +3083,9 @@
30833083
[X86,PV_OPS] Disable paravirtualized VMware scheduler
30843084
clock and use the default one.
30853085

3086-
no-steal-acc [X86,KVM] Disable paravirtualized steal time accounting.
3087-
steal time is computed, but won't influence scheduler
3088-
behaviour
3086+
no-steal-acc [X86,KVM,ARM64] Disable paravirtualized steal time
3087+
accounting. steal time is computed, but won't
3088+
influence scheduler behaviour
30893089

30903090
nolapic [X86-32,APIC] Do not enable or use the local APIC.
30913091

arch/arm64/include/asm/paravirt.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ static inline u64 paravirt_steal_clock(int cpu)
2121
{
2222
return pv_ops.time.steal_clock(cpu);
2323
}
24-
#endif
24+
25+
int __init pv_time_init(void);
26+
27+
#else
28+
29+
#define pv_time_init() do {} while (0)
30+
31+
#endif // CONFIG_PARAVIRT
2532

2633
#endif

arch/arm64/kernel/paravirt.c

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,153 @@
66
* Author: Stefano Stabellini <[email protected]>
77
*/
88

9+
#define pr_fmt(fmt) "arm-pv: " fmt
10+
11+
#include <linux/arm-smccc.h>
12+
#include <linux/cpuhotplug.h>
913
#include <linux/export.h>
14+
#include <linux/io.h>
1015
#include <linux/jump_label.h>
16+
#include <linux/printk.h>
17+
#include <linux/psci.h>
18+
#include <linux/reboot.h>
19+
#include <linux/slab.h>
1120
#include <linux/types.h>
21+
1222
#include <asm/paravirt.h>
23+
#include <asm/pvclock-abi.h>
24+
#include <asm/smp_plat.h>
1325

1426
struct static_key paravirt_steal_enabled;
1527
struct static_key paravirt_steal_rq_enabled;
1628

1729
struct paravirt_patch_template pv_ops;
1830
EXPORT_SYMBOL_GPL(pv_ops);
31+
32+
struct pv_time_stolen_time_region {
33+
struct pvclock_vcpu_stolen_time *kaddr;
34+
};
35+
36+
static DEFINE_PER_CPU(struct pv_time_stolen_time_region, stolen_time_region);
37+
38+
static bool steal_acc = true;
39+
static int __init parse_no_stealacc(char *arg)
40+
{
41+
steal_acc = false;
42+
return 0;
43+
}
44+
45+
early_param("no-steal-acc", parse_no_stealacc);
46+
47+
/* return stolen time in ns by asking the hypervisor */
48+
static u64 pv_steal_clock(int cpu)
49+
{
50+
struct pv_time_stolen_time_region *reg;
51+
52+
reg = per_cpu_ptr(&stolen_time_region, cpu);
53+
if (!reg->kaddr) {
54+
pr_warn_once("stolen time enabled but not configured for cpu %d\n",
55+
cpu);
56+
return 0;
57+
}
58+
59+
return le64_to_cpu(READ_ONCE(reg->kaddr->stolen_time));
60+
}
61+
62+
static int stolen_time_dying_cpu(unsigned int cpu)
63+
{
64+
struct pv_time_stolen_time_region *reg;
65+
66+
reg = this_cpu_ptr(&stolen_time_region);
67+
if (!reg->kaddr)
68+
return 0;
69+
70+
memunmap(reg->kaddr);
71+
memset(reg, 0, sizeof(*reg));
72+
73+
return 0;
74+
}
75+
76+
static int init_stolen_time_cpu(unsigned int cpu)
77+
{
78+
struct pv_time_stolen_time_region *reg;
79+
struct arm_smccc_res res;
80+
81+
reg = this_cpu_ptr(&stolen_time_region);
82+
83+
arm_smccc_1_1_invoke(ARM_SMCCC_HV_PV_TIME_ST, &res);
84+
85+
if (res.a0 == SMCCC_RET_NOT_SUPPORTED)
86+
return -EINVAL;
87+
88+
reg->kaddr = memremap(res.a0,
89+
sizeof(struct pvclock_vcpu_stolen_time),
90+
MEMREMAP_WB);
91+
92+
if (!reg->kaddr) {
93+
pr_warn("Failed to map stolen time data structure\n");
94+
return -ENOMEM;
95+
}
96+
97+
if (le32_to_cpu(reg->kaddr->revision) != 0 ||
98+
le32_to_cpu(reg->kaddr->attributes) != 0) {
99+
pr_warn_once("Unexpected revision or attributes in stolen time data\n");
100+
return -ENXIO;
101+
}
102+
103+
return 0;
104+
}
105+
106+
static int pv_time_init_stolen_time(void)
107+
{
108+
int ret;
109+
110+
ret = cpuhp_setup_state(CPUHP_AP_ARM_KVMPV_STARTING,
111+
"hypervisor/arm/pvtime:starting",
112+
init_stolen_time_cpu, stolen_time_dying_cpu);
113+
if (ret < 0)
114+
return ret;
115+
return 0;
116+
}
117+
118+
static bool has_pv_steal_clock(void)
119+
{
120+
struct arm_smccc_res res;
121+
122+
/* To detect the presence of PV time support we require SMCCC 1.1+ */
123+
if (psci_ops.smccc_version < SMCCC_VERSION_1_1)
124+
return false;
125+
126+
arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
127+
ARM_SMCCC_HV_PV_TIME_FEATURES, &res);
128+
129+
if (res.a0 != SMCCC_RET_SUCCESS)
130+
return false;
131+
132+
arm_smccc_1_1_invoke(ARM_SMCCC_HV_PV_TIME_FEATURES,
133+
ARM_SMCCC_HV_PV_TIME_ST, &res);
134+
135+
return (res.a0 == SMCCC_RET_SUCCESS);
136+
}
137+
138+
int __init pv_time_init(void)
139+
{
140+
int ret;
141+
142+
if (!has_pv_steal_clock())
143+
return 0;
144+
145+
ret = pv_time_init_stolen_time();
146+
if (ret)
147+
return ret;
148+
149+
pv_ops.time.steal_clock = pv_steal_clock;
150+
151+
static_key_slow_inc(&paravirt_steal_enabled);
152+
if (steal_acc)
153+
static_key_slow_inc(&paravirt_steal_rq_enabled);
154+
155+
pr_info("using stolen time PV\n");
156+
157+
return 0;
158+
}

arch/arm64/kernel/time.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
#include <asm/thread_info.h>
3232
#include <asm/stacktrace.h>
33+
#include <asm/paravirt.h>
3334

3435
unsigned long profile_pc(struct pt_regs *regs)
3536
{
@@ -65,4 +66,6 @@ void __init time_init(void)
6566

6667
/* Calibrate the delay loop directly */
6768
lpj_fine = arch_timer_rate / HZ;
69+
70+
pv_time_init();
6871
}

include/linux/cpuhotplug.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ enum cpuhp_state {
136136
/* Must be the last timer callback */
137137
CPUHP_AP_DUMMY_TIMER_STARTING,
138138
CPUHP_AP_ARM_XEN_STARTING,
139+
CPUHP_AP_ARM_KVMPV_STARTING,
139140
CPUHP_AP_ARM_CORESIGHT_STARTING,
140141
CPUHP_AP_ARM64_ISNDEP_STARTING,
141142
CPUHP_AP_SMPCFD_DYING,

0 commit comments

Comments
 (0)