hc
2024-05-14 bedbef8ad3e75a304af6361af235302bcc61d06b
kernel/arch/arm64/kernel/paravirt.c
....@@ -1,25 +1,175 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
2
- * This program is free software; you can redistribute it and/or modify
3
- * it under the terms of the GNU General Public License version 2 as
4
- * published by the Free Software Foundation.
5
- *
6
- * This program is distributed in the hope that it will be useful,
7
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
8
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
- * GNU General Public License for more details.
103 *
114 * Copyright (C) 2013 Citrix Systems
125 *
136 * Author: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
147 */
158
9
+#define pr_fmt(fmt) "arm-pv: " fmt
10
+
11
+#include <linux/arm-smccc.h>
12
+#include <linux/cpuhotplug.h>
1613 #include <linux/export.h>
14
+#include <linux/io.h>
1715 #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>
1820 #include <linux/types.h>
21
+
1922 #include <asm/paravirt.h>
23
+#include <asm/pvclock-abi.h>
24
+#include <asm/smp_plat.h>
2025
2126 struct static_key paravirt_steal_enabled;
2227 struct static_key paravirt_steal_rq_enabled;
2328
24
-struct pv_time_ops pv_time_ops;
25
-EXPORT_SYMBOL_GPL(pv_time_ops);
29
+struct paravirt_patch_template pv_ops;
30
+EXPORT_SYMBOL_GPL(pv_ops);
31
+
32
+struct pv_time_stolen_time_region {
33
+ struct pvclock_vcpu_stolen_time __rcu *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 pvclock_vcpu_stolen_time *kaddr = NULL;
51
+ struct pv_time_stolen_time_region *reg;
52
+ u64 ret = 0;
53
+
54
+ reg = per_cpu_ptr(&stolen_time_region, cpu);
55
+
56
+ /*
57
+ * paravirt_steal_clock() may be called before the CPU
58
+ * online notification callback runs. Until the callback
59
+ * has run we just return zero.
60
+ */
61
+ rcu_read_lock();
62
+ kaddr = rcu_dereference(reg->kaddr);
63
+ if (!kaddr) {
64
+ rcu_read_unlock();
65
+ return 0;
66
+ }
67
+
68
+ ret = le64_to_cpu(READ_ONCE(kaddr->stolen_time));
69
+ rcu_read_unlock();
70
+ return ret;
71
+}
72
+
73
+static int stolen_time_cpu_down_prepare(unsigned int cpu)
74
+{
75
+ struct pvclock_vcpu_stolen_time *kaddr = NULL;
76
+ struct pv_time_stolen_time_region *reg;
77
+
78
+ reg = this_cpu_ptr(&stolen_time_region);
79
+ if (!reg->kaddr)
80
+ return 0;
81
+
82
+ kaddr = rcu_replace_pointer(reg->kaddr, NULL, true);
83
+ synchronize_rcu();
84
+ memunmap(kaddr);
85
+
86
+ return 0;
87
+}
88
+
89
+static int stolen_time_cpu_online(unsigned int cpu)
90
+{
91
+ struct pvclock_vcpu_stolen_time *kaddr = NULL;
92
+ struct pv_time_stolen_time_region *reg;
93
+ struct arm_smccc_res res;
94
+
95
+ reg = this_cpu_ptr(&stolen_time_region);
96
+
97
+ arm_smccc_1_1_invoke(ARM_SMCCC_HV_PV_TIME_ST, &res);
98
+
99
+ if (res.a0 == SMCCC_RET_NOT_SUPPORTED)
100
+ return -EINVAL;
101
+
102
+ kaddr = memremap(res.a0,
103
+ sizeof(struct pvclock_vcpu_stolen_time),
104
+ MEMREMAP_WB);
105
+
106
+ rcu_assign_pointer(reg->kaddr, kaddr);
107
+
108
+ if (!reg->kaddr) {
109
+ pr_warn("Failed to map stolen time data structure\n");
110
+ return -ENOMEM;
111
+ }
112
+
113
+ if (le32_to_cpu(kaddr->revision) != 0 ||
114
+ le32_to_cpu(kaddr->attributes) != 0) {
115
+ pr_warn_once("Unexpected revision or attributes in stolen time data\n");
116
+ return -ENXIO;
117
+ }
118
+
119
+ return 0;
120
+}
121
+
122
+static int __init pv_time_init_stolen_time(void)
123
+{
124
+ int ret;
125
+
126
+ ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
127
+ "hypervisor/arm/pvtime:online",
128
+ stolen_time_cpu_online,
129
+ stolen_time_cpu_down_prepare);
130
+ if (ret < 0)
131
+ return ret;
132
+ return 0;
133
+}
134
+
135
+static bool __init has_pv_steal_clock(void)
136
+{
137
+ struct arm_smccc_res res;
138
+
139
+ /* To detect the presence of PV time support we require SMCCC 1.1+ */
140
+ if (arm_smccc_1_1_get_conduit() == SMCCC_CONDUIT_NONE)
141
+ return false;
142
+
143
+ arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
144
+ ARM_SMCCC_HV_PV_TIME_FEATURES, &res);
145
+
146
+ if (res.a0 != SMCCC_RET_SUCCESS)
147
+ return false;
148
+
149
+ arm_smccc_1_1_invoke(ARM_SMCCC_HV_PV_TIME_FEATURES,
150
+ ARM_SMCCC_HV_PV_TIME_ST, &res);
151
+
152
+ return (res.a0 == SMCCC_RET_SUCCESS);
153
+}
154
+
155
+int __init pv_time_init(void)
156
+{
157
+ int ret;
158
+
159
+ if (!has_pv_steal_clock())
160
+ return 0;
161
+
162
+ ret = pv_time_init_stolen_time();
163
+ if (ret)
164
+ return ret;
165
+
166
+ pv_ops.time.steal_clock = pv_steal_clock;
167
+
168
+ static_key_slow_inc(&paravirt_steal_enabled);
169
+ if (steal_acc)
170
+ static_key_slow_inc(&paravirt_steal_rq_enabled);
171
+
172
+ pr_info("using stolen time PV\n");
173
+
174
+ return 0;
175
+}