diff options
author | Marc Zyngier <marc.zyngier@arm.com> | 2013-05-14 09:31:01 -0400 |
---|---|---|
committer | Gleb Natapov <gleb@redhat.com> | 2013-05-19 08:13:08 -0400 |
commit | 7275acdfe29ba03ad2f6e150386900c4e2d43fb1 (patch) | |
tree | 453d397f2d35e6f7f432fbd72f4b370f2606b665 /virt/kvm/arm/arch_timer.c | |
parent | 35af577aacb2fd2a6b407953044aea1cf0546fad (diff) |
ARM: KVM: move GIC/timer code to a common location
As KVM/arm64 is looming on the horizon, it makes sense to move some
of the common code to a single location in order to reduce duplication.
The code could live anywhere. Actually, most of KVM is already built
with a bunch of ugly ../../.. hacks in the various Makefiles, so we're
not exactly talking about style here. But maybe it is time to start
moving into a less ugly direction.
The include files must be in a "public" location, as they are accessed
from non-KVM files (arch/arm/kernel/asm-offsets.c).
For this purpose, introduce two new locations:
- virt/kvm/arm/ : x86 and ia64 already share the ioapic code in
virt/kvm, so this could be seen as a (very ugly) precedent.
- include/kvm/ : there is already an include/xen, and while the
intent is slightly different, this seems as good a location as
any
Eventually, we should probably have independant Makefiles at every
levels (just like everywhere else in the kernel), but this is just
the first step.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Gleb Natapov <gleb@redhat.com>
Diffstat (limited to 'virt/kvm/arm/arch_timer.c')
-rw-r--r-- | virt/kvm/arm/arch_timer.c | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c new file mode 100644 index 000000000000..2d00b2925780 --- /dev/null +++ b/virt/kvm/arm/arch_timer.c | |||
@@ -0,0 +1,272 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 ARM Ltd. | ||
3 | * Author: Marc Zyngier <marc.zyngier@arm.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
17 | */ | ||
18 | |||
19 | #include <linux/cpu.h> | ||
20 | #include <linux/of_irq.h> | ||
21 | #include <linux/kvm.h> | ||
22 | #include <linux/kvm_host.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | |||
25 | #include <clocksource/arm_arch_timer.h> | ||
26 | #include <asm/arch_timer.h> | ||
27 | |||
28 | #include <kvm/arm_vgic.h> | ||
29 | #include <kvm/arm_arch_timer.h> | ||
30 | |||
31 | static struct timecounter *timecounter; | ||
32 | static struct workqueue_struct *wqueue; | ||
33 | static struct kvm_irq_level timer_irq = { | ||
34 | .level = 1, | ||
35 | }; | ||
36 | |||
37 | static cycle_t kvm_phys_timer_read(void) | ||
38 | { | ||
39 | return timecounter->cc->read(timecounter->cc); | ||
40 | } | ||
41 | |||
42 | static bool timer_is_armed(struct arch_timer_cpu *timer) | ||
43 | { | ||
44 | return timer->armed; | ||
45 | } | ||
46 | |||
47 | /* timer_arm: as in "arm the timer", not as in ARM the company */ | ||
48 | static void timer_arm(struct arch_timer_cpu *timer, u64 ns) | ||
49 | { | ||
50 | timer->armed = true; | ||
51 | hrtimer_start(&timer->timer, ktime_add_ns(ktime_get(), ns), | ||
52 | HRTIMER_MODE_ABS); | ||
53 | } | ||
54 | |||
55 | static void timer_disarm(struct arch_timer_cpu *timer) | ||
56 | { | ||
57 | if (timer_is_armed(timer)) { | ||
58 | hrtimer_cancel(&timer->timer); | ||
59 | cancel_work_sync(&timer->expired); | ||
60 | timer->armed = false; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | static void kvm_timer_inject_irq(struct kvm_vcpu *vcpu) | ||
65 | { | ||
66 | struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; | ||
67 | |||
68 | timer->cntv_ctl |= ARCH_TIMER_CTRL_IT_MASK; | ||
69 | kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id, | ||
70 | vcpu->arch.timer_cpu.irq->irq, | ||
71 | vcpu->arch.timer_cpu.irq->level); | ||
72 | } | ||
73 | |||
74 | static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id) | ||
75 | { | ||
76 | struct kvm_vcpu *vcpu = *(struct kvm_vcpu **)dev_id; | ||
77 | |||
78 | /* | ||
79 | * We disable the timer in the world switch and let it be | ||
80 | * handled by kvm_timer_sync_hwstate(). Getting a timer | ||
81 | * interrupt at this point is a sure sign of some major | ||
82 | * breakage. | ||
83 | */ | ||
84 | pr_warn("Unexpected interrupt %d on vcpu %p\n", irq, vcpu); | ||
85 | return IRQ_HANDLED; | ||
86 | } | ||
87 | |||
88 | static void kvm_timer_inject_irq_work(struct work_struct *work) | ||
89 | { | ||
90 | struct kvm_vcpu *vcpu; | ||
91 | |||
92 | vcpu = container_of(work, struct kvm_vcpu, arch.timer_cpu.expired); | ||
93 | vcpu->arch.timer_cpu.armed = false; | ||
94 | kvm_timer_inject_irq(vcpu); | ||
95 | } | ||
96 | |||
97 | static enum hrtimer_restart kvm_timer_expire(struct hrtimer *hrt) | ||
98 | { | ||
99 | struct arch_timer_cpu *timer; | ||
100 | timer = container_of(hrt, struct arch_timer_cpu, timer); | ||
101 | queue_work(wqueue, &timer->expired); | ||
102 | return HRTIMER_NORESTART; | ||
103 | } | ||
104 | |||
105 | /** | ||
106 | * kvm_timer_flush_hwstate - prepare to move the virt timer to the cpu | ||
107 | * @vcpu: The vcpu pointer | ||
108 | * | ||
109 | * Disarm any pending soft timers, since the world-switch code will write the | ||
110 | * virtual timer state back to the physical CPU. | ||
111 | */ | ||
112 | void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) | ||
113 | { | ||
114 | struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; | ||
115 | |||
116 | /* | ||
117 | * We're about to run this vcpu again, so there is no need to | ||
118 | * keep the background timer running, as we're about to | ||
119 | * populate the CPU timer again. | ||
120 | */ | ||
121 | timer_disarm(timer); | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * kvm_timer_sync_hwstate - sync timer state from cpu | ||
126 | * @vcpu: The vcpu pointer | ||
127 | * | ||
128 | * Check if the virtual timer was armed and either schedule a corresponding | ||
129 | * soft timer or inject directly if already expired. | ||
130 | */ | ||
131 | void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) | ||
132 | { | ||
133 | struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; | ||
134 | cycle_t cval, now; | ||
135 | u64 ns; | ||
136 | |||
137 | if ((timer->cntv_ctl & ARCH_TIMER_CTRL_IT_MASK) || | ||
138 | !(timer->cntv_ctl & ARCH_TIMER_CTRL_ENABLE)) | ||
139 | return; | ||
140 | |||
141 | cval = timer->cntv_cval; | ||
142 | now = kvm_phys_timer_read() - vcpu->kvm->arch.timer.cntvoff; | ||
143 | |||
144 | BUG_ON(timer_is_armed(timer)); | ||
145 | |||
146 | if (cval <= now) { | ||
147 | /* | ||
148 | * Timer has already expired while we were not | ||
149 | * looking. Inject the interrupt and carry on. | ||
150 | */ | ||
151 | kvm_timer_inject_irq(vcpu); | ||
152 | return; | ||
153 | } | ||
154 | |||
155 | ns = cyclecounter_cyc2ns(timecounter->cc, cval - now); | ||
156 | timer_arm(timer, ns); | ||
157 | } | ||
158 | |||
159 | void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) | ||
160 | { | ||
161 | struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; | ||
162 | |||
163 | INIT_WORK(&timer->expired, kvm_timer_inject_irq_work); | ||
164 | hrtimer_init(&timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); | ||
165 | timer->timer.function = kvm_timer_expire; | ||
166 | timer->irq = &timer_irq; | ||
167 | } | ||
168 | |||
169 | static void kvm_timer_init_interrupt(void *info) | ||
170 | { | ||
171 | enable_percpu_irq(timer_irq.irq, 0); | ||
172 | } | ||
173 | |||
174 | |||
175 | static int kvm_timer_cpu_notify(struct notifier_block *self, | ||
176 | unsigned long action, void *cpu) | ||
177 | { | ||
178 | switch (action) { | ||
179 | case CPU_STARTING: | ||
180 | case CPU_STARTING_FROZEN: | ||
181 | kvm_timer_init_interrupt(NULL); | ||
182 | break; | ||
183 | case CPU_DYING: | ||
184 | case CPU_DYING_FROZEN: | ||
185 | disable_percpu_irq(timer_irq.irq); | ||
186 | break; | ||
187 | } | ||
188 | |||
189 | return NOTIFY_OK; | ||
190 | } | ||
191 | |||
192 | static struct notifier_block kvm_timer_cpu_nb = { | ||
193 | .notifier_call = kvm_timer_cpu_notify, | ||
194 | }; | ||
195 | |||
196 | static const struct of_device_id arch_timer_of_match[] = { | ||
197 | { .compatible = "arm,armv7-timer", }, | ||
198 | {}, | ||
199 | }; | ||
200 | |||
201 | int kvm_timer_hyp_init(void) | ||
202 | { | ||
203 | struct device_node *np; | ||
204 | unsigned int ppi; | ||
205 | int err; | ||
206 | |||
207 | timecounter = arch_timer_get_timecounter(); | ||
208 | if (!timecounter) | ||
209 | return -ENODEV; | ||
210 | |||
211 | np = of_find_matching_node(NULL, arch_timer_of_match); | ||
212 | if (!np) { | ||
213 | kvm_err("kvm_arch_timer: can't find DT node\n"); | ||
214 | return -ENODEV; | ||
215 | } | ||
216 | |||
217 | ppi = irq_of_parse_and_map(np, 2); | ||
218 | if (!ppi) { | ||
219 | kvm_err("kvm_arch_timer: no virtual timer interrupt\n"); | ||
220 | err = -EINVAL; | ||
221 | goto out; | ||
222 | } | ||
223 | |||
224 | err = request_percpu_irq(ppi, kvm_arch_timer_handler, | ||
225 | "kvm guest timer", kvm_get_running_vcpus()); | ||
226 | if (err) { | ||
227 | kvm_err("kvm_arch_timer: can't request interrupt %d (%d)\n", | ||
228 | ppi, err); | ||
229 | goto out; | ||
230 | } | ||
231 | |||
232 | timer_irq.irq = ppi; | ||
233 | |||
234 | err = register_cpu_notifier(&kvm_timer_cpu_nb); | ||
235 | if (err) { | ||
236 | kvm_err("Cannot register timer CPU notifier\n"); | ||
237 | goto out_free; | ||
238 | } | ||
239 | |||
240 | wqueue = create_singlethread_workqueue("kvm_arch_timer"); | ||
241 | if (!wqueue) { | ||
242 | err = -ENOMEM; | ||
243 | goto out_free; | ||
244 | } | ||
245 | |||
246 | kvm_info("%s IRQ%d\n", np->name, ppi); | ||
247 | on_each_cpu(kvm_timer_init_interrupt, NULL, 1); | ||
248 | |||
249 | goto out; | ||
250 | out_free: | ||
251 | free_percpu_irq(ppi, kvm_get_running_vcpus()); | ||
252 | out: | ||
253 | of_node_put(np); | ||
254 | return err; | ||
255 | } | ||
256 | |||
257 | void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu) | ||
258 | { | ||
259 | struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; | ||
260 | |||
261 | timer_disarm(timer); | ||
262 | } | ||
263 | |||
264 | int kvm_timer_init(struct kvm *kvm) | ||
265 | { | ||
266 | if (timecounter && wqueue) { | ||
267 | kvm->arch.timer.cntvoff = kvm_phys_timer_read(); | ||
268 | kvm->arch.timer.enabled = 1; | ||
269 | } | ||
270 | |||
271 | return 0; | ||
272 | } | ||