diff options
| author | Christoffer Dall <christoffer.dall@arm.com> | 2019-01-04 07:31:22 -0500 |
|---|---|---|
| committer | Marc Zyngier <marc.zyngier@arm.com> | 2019-02-19 16:05:43 -0500 |
| commit | bee038a67487598ebbe995f85bf60c3a5b2e9099 (patch) | |
| tree | c0756ab3d7bc4822adff585a482c940135f4db6e | |
| parent | 9e01dc76be6a3b5768cb02130d2ff0055a68809a (diff) | |
KVM: arm/arm64: Rework the timer code to use a timer_map
We are currently emulating two timers in two different ways. When we
add support for nested virtualization in the future, we are going to be
emulating either two timers in two diffferent ways, or four timers in a
single way.
We need a unified data structure to keep track of how we map virtual
state to physical state and we need to cleanup some of the timer code to
operate more independently on a struct arch_timer_context instead of
trying to consider the global state of the VCPU and recomputing all
state.
Co-written with Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Christoffer Dall <christoffer.dall@arm.com>
| -rw-r--r-- | include/kvm/arm_arch_timer.h | 23 | ||||
| -rw-r--r-- | virt/kvm/arm/arch_timer.c | 295 | ||||
| -rw-r--r-- | virt/kvm/arm/trace.h | 105 |
3 files changed, 278 insertions, 145 deletions
diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h index 6d4a33a9c45a..05a18dd265b5 100644 --- a/include/kvm/arm_arch_timer.h +++ b/include/kvm/arm_arch_timer.h | |||
| @@ -51,11 +51,24 @@ struct arch_timer_context { | |||
| 51 | /* Emulated Timer (may be unused) */ | 51 | /* Emulated Timer (may be unused) */ |
| 52 | struct hrtimer hrtimer; | 52 | struct hrtimer hrtimer; |
| 53 | 53 | ||
| 54 | /* | ||
| 55 | * We have multiple paths which can save/restore the timer state onto | ||
| 56 | * the hardware, so we need some way of keeping track of where the | ||
| 57 | * latest state is. | ||
| 58 | */ | ||
| 59 | bool loaded; | ||
| 60 | |||
| 54 | /* Duplicated state from arch_timer.c for convenience */ | 61 | /* Duplicated state from arch_timer.c for convenience */ |
| 55 | u32 host_timer_irq; | 62 | u32 host_timer_irq; |
| 56 | u32 host_timer_irq_flags; | 63 | u32 host_timer_irq_flags; |
| 57 | }; | 64 | }; |
| 58 | 65 | ||
| 66 | struct timer_map { | ||
| 67 | struct arch_timer_context *direct_vtimer; | ||
| 68 | struct arch_timer_context *direct_ptimer; | ||
| 69 | struct arch_timer_context *emul_ptimer; | ||
| 70 | }; | ||
| 71 | |||
| 59 | struct arch_timer_cpu { | 72 | struct arch_timer_cpu { |
| 60 | struct arch_timer_context timers[NR_KVM_TIMERS]; | 73 | struct arch_timer_context timers[NR_KVM_TIMERS]; |
| 61 | 74 | ||
| @@ -64,16 +77,6 @@ struct arch_timer_cpu { | |||
| 64 | 77 | ||
| 65 | /* Is the timer enabled */ | 78 | /* Is the timer enabled */ |
| 66 | bool enabled; | 79 | bool enabled; |
| 67 | |||
| 68 | /* | ||
| 69 | * We have multiple paths which can save/restore the timer state | ||
| 70 | * onto the hardware, so we need some way of keeping track of | ||
| 71 | * where the latest state is. | ||
| 72 | * | ||
| 73 | * loaded == true: State is loaded on the hardware registers. | ||
| 74 | * loaded == false: State is stored in memory. | ||
| 75 | */ | ||
| 76 | bool loaded; | ||
| 77 | }; | 80 | }; |
| 78 | 81 | ||
| 79 | int kvm_timer_hyp_init(bool); | 82 | int kvm_timer_hyp_init(bool); |
diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c index 10c15151c87e..17f9de73cc8a 100644 --- a/virt/kvm/arm/arch_timer.c +++ b/virt/kvm/arm/arch_timer.c | |||
| @@ -68,6 +68,21 @@ u64 kvm_phys_timer_read(void) | |||
| 68 | return timecounter->cc->read(timecounter->cc); | 68 | return timecounter->cc->read(timecounter->cc); |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | static void get_timer_map(struct kvm_vcpu *vcpu, struct timer_map *map) | ||
| 72 | { | ||
| 73 | if (has_vhe()) { | ||
| 74 | map->direct_vtimer = vcpu_vtimer(vcpu); | ||
| 75 | map->direct_ptimer = vcpu_ptimer(vcpu); | ||
| 76 | map->emul_ptimer = NULL; | ||
| 77 | } else { | ||
| 78 | map->direct_vtimer = vcpu_vtimer(vcpu); | ||
| 79 | map->direct_ptimer = NULL; | ||
| 80 | map->emul_ptimer = vcpu_ptimer(vcpu); | ||
| 81 | } | ||
| 82 | |||
| 83 | trace_kvm_get_timer_map(vcpu->vcpu_id, map); | ||
| 84 | } | ||
| 85 | |||
| 71 | static inline bool userspace_irqchip(struct kvm *kvm) | 86 | static inline bool userspace_irqchip(struct kvm *kvm) |
| 72 | { | 87 | { |
| 73 | return static_branch_unlikely(&userspace_irqchip_in_use) && | 88 | return static_branch_unlikely(&userspace_irqchip_in_use) && |
| @@ -89,6 +104,7 @@ static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id) | |||
| 89 | { | 104 | { |
| 90 | struct kvm_vcpu *vcpu = *(struct kvm_vcpu **)dev_id; | 105 | struct kvm_vcpu *vcpu = *(struct kvm_vcpu **)dev_id; |
| 91 | struct arch_timer_context *ctx; | 106 | struct arch_timer_context *ctx; |
| 107 | struct timer_map map; | ||
| 92 | 108 | ||
| 93 | /* | 109 | /* |
| 94 | * We may see a timer interrupt after vcpu_put() has been called which | 110 | * We may see a timer interrupt after vcpu_put() has been called which |
| @@ -99,10 +115,12 @@ static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id) | |||
| 99 | if (!vcpu) | 115 | if (!vcpu) |
| 100 | return IRQ_HANDLED; | 116 | return IRQ_HANDLED; |
| 101 | 117 | ||
| 118 | get_timer_map(vcpu, &map); | ||
| 119 | |||
| 102 | if (irq == host_vtimer_irq) | 120 | if (irq == host_vtimer_irq) |
| 103 | ctx = vcpu_vtimer(vcpu); | 121 | ctx = map.direct_vtimer; |
| 104 | else | 122 | else |
| 105 | ctx = vcpu_ptimer(vcpu); | 123 | ctx = map.direct_ptimer; |
| 106 | 124 | ||
| 107 | if (kvm_timer_should_fire(ctx)) | 125 | if (kvm_timer_should_fire(ctx)) |
| 108 | kvm_timer_update_irq(vcpu, true, ctx); | 126 | kvm_timer_update_irq(vcpu, true, ctx); |
| @@ -136,7 +154,9 @@ static u64 kvm_timer_compute_delta(struct arch_timer_context *timer_ctx) | |||
| 136 | 154 | ||
| 137 | static bool kvm_timer_irq_can_fire(struct arch_timer_context *timer_ctx) | 155 | static bool kvm_timer_irq_can_fire(struct arch_timer_context *timer_ctx) |
| 138 | { | 156 | { |
| 139 | return !(timer_ctx->cnt_ctl & ARCH_TIMER_CTRL_IT_MASK) && | 157 | WARN_ON(timer_ctx && timer_ctx->loaded); |
| 158 | return timer_ctx && | ||
| 159 | !(timer_ctx->cnt_ctl & ARCH_TIMER_CTRL_IT_MASK) && | ||
| 140 | (timer_ctx->cnt_ctl & ARCH_TIMER_CTRL_ENABLE); | 160 | (timer_ctx->cnt_ctl & ARCH_TIMER_CTRL_ENABLE); |
| 141 | } | 161 | } |
| 142 | 162 | ||
| @@ -146,21 +166,22 @@ static bool kvm_timer_irq_can_fire(struct arch_timer_context *timer_ctx) | |||
| 146 | */ | 166 | */ |
| 147 | static u64 kvm_timer_earliest_exp(struct kvm_vcpu *vcpu) | 167 | static u64 kvm_timer_earliest_exp(struct kvm_vcpu *vcpu) |
| 148 | { | 168 | { |
| 149 | u64 min_virt = ULLONG_MAX, min_phys = ULLONG_MAX; | 169 | u64 min_delta = ULLONG_MAX; |
| 150 | struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); | 170 | int i; |
| 151 | struct arch_timer_context *ptimer = vcpu_ptimer(vcpu); | ||
| 152 | 171 | ||
| 153 | if (kvm_timer_irq_can_fire(vtimer)) | 172 | for (i = 0; i < NR_KVM_TIMERS; i++) { |
| 154 | min_virt = kvm_timer_compute_delta(vtimer); | 173 | struct arch_timer_context *ctx = &vcpu->arch.timer_cpu.timers[i]; |
| 155 | 174 | ||
| 156 | if (kvm_timer_irq_can_fire(ptimer)) | 175 | WARN(ctx->loaded, "timer %d loaded\n", i); |
| 157 | min_phys = kvm_timer_compute_delta(ptimer); | 176 | if (kvm_timer_irq_can_fire(ctx)) |
| 177 | min_delta = min(min_delta, kvm_timer_compute_delta(ctx)); | ||
| 178 | } | ||
| 158 | 179 | ||
| 159 | /* If none of timers can fire, then return 0 */ | 180 | /* If none of timers can fire, then return 0 */ |
| 160 | if ((min_virt == ULLONG_MAX) && (min_phys == ULLONG_MAX)) | 181 | if (min_delta == ULLONG_MAX) |
| 161 | return 0; | 182 | return 0; |
| 162 | 183 | ||
| 163 | return min(min_virt, min_phys); | 184 | return min_delta; |
| 164 | } | 185 | } |
| 165 | 186 | ||
| 166 | static enum hrtimer_restart kvm_bg_timer_expire(struct hrtimer *hrt) | 187 | static enum hrtimer_restart kvm_bg_timer_expire(struct hrtimer *hrt) |
| @@ -187,37 +208,45 @@ static enum hrtimer_restart kvm_bg_timer_expire(struct hrtimer *hrt) | |||
| 187 | return HRTIMER_NORESTART; | 208 | return HRTIMER_NORESTART; |
| 188 | } | 209 | } |
| 189 | 210 | ||
| 190 | static enum hrtimer_restart kvm_phys_timer_expire(struct hrtimer *hrt) | 211 | static enum hrtimer_restart kvm_hrtimer_expire(struct hrtimer *hrt) |
| 191 | { | 212 | { |
| 192 | struct arch_timer_context *ptimer; | 213 | struct arch_timer_context *ctx; |
| 193 | struct kvm_vcpu *vcpu; | 214 | struct kvm_vcpu *vcpu; |
| 194 | u64 ns; | 215 | u64 ns; |
| 195 | 216 | ||
| 196 | ptimer = container_of(hrt, struct arch_timer_context, hrtimer); | 217 | ctx = container_of(hrt, struct arch_timer_context, hrtimer); |
| 197 | vcpu = ptimer->vcpu; | 218 | vcpu = ctx->vcpu; |
| 219 | |||
| 220 | trace_kvm_timer_hrtimer_expire(ctx); | ||
| 198 | 221 | ||
| 199 | /* | 222 | /* |
| 200 | * Check that the timer has really expired from the guest's | 223 | * Check that the timer has really expired from the guest's |
| 201 | * PoV (NTP on the host may have forced it to expire | 224 | * PoV (NTP on the host may have forced it to expire |
| 202 | * early). If not ready, schedule for a later time. | 225 | * early). If not ready, schedule for a later time. |
| 203 | */ | 226 | */ |
| 204 | ns = kvm_timer_compute_delta(ptimer); | 227 | ns = kvm_timer_compute_delta(ctx); |
| 205 | if (unlikely(ns)) { | 228 | if (unlikely(ns)) { |
| 206 | hrtimer_forward_now(hrt, ns_to_ktime(ns)); | 229 | hrtimer_forward_now(hrt, ns_to_ktime(ns)); |
| 207 | return HRTIMER_RESTART; | 230 | return HRTIMER_RESTART; |
| 208 | } | 231 | } |
| 209 | 232 | ||
| 210 | kvm_timer_update_irq(vcpu, true, ptimer); | 233 | kvm_timer_update_irq(vcpu, true, ctx); |
| 211 | return HRTIMER_NORESTART; | 234 | return HRTIMER_NORESTART; |
| 212 | } | 235 | } |
| 213 | 236 | ||
| 214 | static bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx) | 237 | static bool kvm_timer_sho |
