A late round of KVM/ARM fixes for v4.3-rc7, fixing:
- A bug where level-triggered interrupts lowered from userspace are still routed to the guest - A memory leak an a failed initialization path - A build error under certain configurations - Several timer bugs introduced with moving the timer to the active state handling instead of the masking trick. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEbBAABAgAGBQJWJmf7AAoJEEtpOizt6ddy35UH+NCAt1AInvQajAB1rzXzFSYh v9PQ5zJ7ZlP8M3HFYjerSl0HrGbi2Cwij5KYE+UAQkXIzCTTdDeB9TUSI/GYQ82M axel8Ob3deELp03nS5I+gP1NGX3HPDz445grScg7yl1lMs3uDy4viDDM8Vm8+HUX zZWGpDRHNW3b0myS5NVJvOP7Sm5t7DqklsNScZ04Nut7V8gYEUqEu+ZNKerIZkKn LrJUVYqqZf33bf6pWle95QZuNQtg84kion3qwWaNl+/8jFkAA8CODyvzar9VMHpf xA0xUkK1ERZ+hw2n0/BIgCxI4lXvROAx0YpZKnSMgEU5E5p5gGZYqlHCw8r1Rw== =VQSu -----END PGP SIGNATURE----- Merge tag 'kvm-arm-for-v4.3-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm into kvm-master A late round of KVM/ARM fixes for v4.3-rc7, fixing: - A bug where level-triggered interrupts lowered from userspace are still routed to the guest - A memory leak an a failed initialization path - A build error under certain configurations - Several timer bugs introduced with moving the timer to the active state handling instead of the masking trick.
This commit is contained in:
commit
ad355e383d
|
@ -21,6 +21,7 @@ config KVM
|
|||
depends on MMU && OF
|
||||
select PREEMPT_NOTIFIERS
|
||||
select ANON_INODES
|
||||
select ARM_GIC
|
||||
select HAVE_KVM_CPU_RELAX_INTERCEPT
|
||||
select HAVE_KVM_ARCH_TLB_FLUSH_ALL
|
||||
select KVM_MMIO
|
||||
|
|
|
@ -1080,7 +1080,7 @@ static int init_hyp_mode(void)
|
|||
*/
|
||||
err = kvm_timer_hyp_init();
|
||||
if (err)
|
||||
goto out_free_mappings;
|
||||
goto out_free_context;
|
||||
|
||||
#ifndef CONFIG_HOTPLUG_CPU
|
||||
free_boot_hyp_pgd();
|
||||
|
|
|
@ -137,6 +137,8 @@ bool kvm_timer_should_fire(struct kvm_vcpu *vcpu)
|
|||
void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
|
||||
bool phys_active;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* We're about to run this vcpu again, so there is no need to
|
||||
|
@ -151,6 +153,23 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
|
|||
*/
|
||||
if (kvm_timer_should_fire(vcpu))
|
||||
kvm_timer_inject_irq(vcpu);
|
||||
|
||||
/*
|
||||
* We keep track of whether the edge-triggered interrupt has been
|
||||
* signalled to the vgic/guest, and if so, we mask the interrupt and
|
||||
* the physical distributor to prevent the timer from raising a
|
||||
* physical interrupt whenever we run a guest, preventing forward
|
||||
* VCPU progress.
|
||||
*/
|
||||
if (kvm_vgic_get_phys_irq_active(timer->map))
|
||||
phys_active = true;
|
||||
else
|
||||
phys_active = false;
|
||||
|
||||
ret = irq_set_irqchip_state(timer->map->irq,
|
||||
IRQCHIP_STATE_ACTIVE,
|
||||
phys_active);
|
||||
WARN_ON(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -531,6 +531,34 @@ bool vgic_handle_set_pending_reg(struct kvm *kvm,
|
|||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* If a mapped interrupt's state has been modified by the guest such that it
|
||||
* is no longer active or pending, without it have gone through the sync path,
|
||||
* then the map->active field must be cleared so the interrupt can be taken
|
||||
* again.
|
||||
*/
|
||||
static void vgic_handle_clear_mapped_irq(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
struct list_head *root;
|
||||
struct irq_phys_map_entry *entry;
|
||||
struct irq_phys_map *map;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
/* Check for PPIs */
|
||||
root = &vgic_cpu->irq_phys_map_list;
|
||||
list_for_each_entry_rcu(entry, root, entry) {
|
||||
map = &entry->map;
|
||||
|
||||
if (!vgic_dist_irq_is_pending(vcpu, map->virt_irq) &&
|
||||
!vgic_irq_is_active(vcpu, map->virt_irq))
|
||||
map->active = false;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
bool vgic_handle_clear_pending_reg(struct kvm *kvm,
|
||||
struct kvm_exit_mmio *mmio,
|
||||
phys_addr_t offset, int vcpu_id)
|
||||
|
@ -561,6 +589,7 @@ bool vgic_handle_clear_pending_reg(struct kvm *kvm,
|
|||
vcpu_id, offset);
|
||||
vgic_reg_access(mmio, reg, offset, mode);
|
||||
|
||||
vgic_handle_clear_mapped_irq(kvm_get_vcpu(kvm, vcpu_id));
|
||||
vgic_update_state(kvm);
|
||||
return true;
|
||||
}
|
||||
|
@ -598,6 +627,7 @@ bool vgic_handle_clear_active_reg(struct kvm *kvm,
|
|||
ACCESS_READ_VALUE | ACCESS_WRITE_CLEARBIT);
|
||||
|
||||
if (mmio->is_write) {
|
||||
vgic_handle_clear_mapped_irq(kvm_get_vcpu(kvm, vcpu_id));
|
||||
vgic_update_state(kvm);
|
||||
return true;
|
||||
}
|
||||
|
@ -982,6 +1012,12 @@ static int compute_pending_for_cpu(struct kvm_vcpu *vcpu)
|
|||
pend_percpu = vcpu->arch.vgic_cpu.pending_percpu;
|
||||
pend_shared = vcpu->arch.vgic_cpu.pending_shared;
|
||||
|
||||
if (!dist->enabled) {
|
||||
bitmap_zero(pend_percpu, VGIC_NR_PRIVATE_IRQS);
|
||||
bitmap_zero(pend_shared, nr_shared);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pending = vgic_bitmap_get_cpu_map(&dist->irq_pending, vcpu_id);
|
||||
enabled = vgic_bitmap_get_cpu_map(&dist->irq_enabled, vcpu_id);
|
||||
bitmap_and(pend_percpu, pending, enabled, VGIC_NR_PRIVATE_IRQS);
|
||||
|
@ -1009,11 +1045,6 @@ void vgic_update_state(struct kvm *kvm)
|
|||
struct kvm_vcpu *vcpu;
|
||||
int c;
|
||||
|
||||
if (!dist->enabled) {
|
||||
set_bit(0, dist->irq_pending_on_cpu);
|
||||
return;
|
||||
}
|
||||
|
||||
kvm_for_each_vcpu(c, vcpu, kvm) {
|
||||
if (compute_pending_for_cpu(vcpu))
|
||||
set_bit(c, dist->irq_pending_on_cpu);
|
||||
|
@ -1092,6 +1123,15 @@ static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu)
|
|||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
struct vgic_lr vlr = vgic_get_lr(vcpu, lr_nr);
|
||||
|
||||
/*
|
||||
* We must transfer the pending state back to the distributor before
|
||||
* retiring the LR, otherwise we may loose edge-triggered interrupts.
|
||||
*/
|
||||
if (vlr.state & LR_STATE_PENDING) {
|
||||
vgic_dist_irq_set_pending(vcpu, irq);
|
||||
vlr.hwirq = 0;
|
||||
}
|
||||
|
||||
vlr.state = 0;
|
||||
vgic_set_lr(vcpu, lr_nr, vlr);
|
||||
clear_bit(lr_nr, vgic_cpu->lr_used);
|
||||
|
@ -1132,7 +1172,8 @@ static void vgic_queue_irq_to_lr(struct kvm_vcpu *vcpu, int irq,
|
|||
kvm_debug("Set active, clear distributor: 0x%x\n", vlr.state);
|
||||
vgic_irq_clear_active(vcpu, irq);
|
||||
vgic_update_state(vcpu->kvm);
|
||||
} else if (vgic_dist_irq_is_pending(vcpu, irq)) {
|
||||
} else {
|
||||
WARN_ON(!vgic_dist_irq_is_pending(vcpu, irq));
|
||||
vlr.state |= LR_STATE_PENDING;
|
||||
kvm_debug("Set pending: 0x%x\n", vlr.state);
|
||||
}
|
||||
|
@ -1240,7 +1281,7 @@ static void __kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
|
|||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
||||
unsigned long *pa_percpu, *pa_shared;
|
||||
int i, vcpu_id, lr, ret;
|
||||
int i, vcpu_id;
|
||||
int overflow = 0;
|
||||
int nr_shared = vgic_nr_shared_irqs(dist);
|
||||
|
||||
|
@ -1295,31 +1336,6 @@ epilog:
|
|||
*/
|
||||
clear_bit(vcpu_id, dist->irq_pending_on_cpu);
|
||||
}
|
||||
|
||||
for (lr = 0; lr < vgic->nr_lr; lr++) {
|
||||
struct vgic_lr vlr;
|
||||
|
||||
if (!test_bit(lr, vgic_cpu->lr_used))
|
||||
continue;
|
||||
|
||||
vlr = vgic_get_lr(vcpu, lr);
|
||||
|
||||
/*
|
||||
* If we have a mapping, and the virtual interrupt is
|
||||
* presented to the guest (as pending or active), then we must
|
||||
* set the state to active in the physical world. See
|
||||
* Documentation/virtual/kvm/arm/vgic-mapped-irqs.txt.
|
||||
*/
|
||||
if (vlr.state & LR_HW) {
|
||||
struct irq_phys_map *map;
|
||||
map = vgic_irq_map_search(vcpu, vlr.irq);
|
||||
|
||||
ret = irq_set_irqchip_state(map->irq,
|
||||
IRQCHIP_STATE_ACTIVE,
|
||||
true);
|
||||
WARN_ON(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool vgic_process_maintenance(struct kvm_vcpu *vcpu)
|
||||
|
@ -1421,7 +1437,7 @@ static int vgic_sync_hwirq(struct kvm_vcpu *vcpu, struct vgic_lr vlr)
|
|||
return 0;
|
||||
|
||||
map = vgic_irq_map_search(vcpu, vlr.irq);
|
||||
BUG_ON(!map || !map->active);
|
||||
BUG_ON(!map);
|
||||
|
||||
ret = irq_get_irqchip_state(map->irq,
|
||||
IRQCHIP_STATE_ACTIVE,
|
||||
|
@ -1429,13 +1445,8 @@ static int vgic_sync_hwirq(struct kvm_vcpu *vcpu, struct vgic_lr vlr)
|
|||
|
||||
WARN_ON(ret);
|
||||
|
||||
if (map->active) {
|
||||
ret = irq_set_irqchip_state(map->irq,
|
||||
IRQCHIP_STATE_ACTIVE,
|
||||
false);
|
||||
WARN_ON(ret);
|
||||
if (map->active)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -1607,8 +1618,12 @@ static int vgic_update_irq_pending(struct kvm *kvm, int cpuid,
|
|||
} else {
|
||||
if (level_triggered) {
|
||||
vgic_dist_irq_clear_level(vcpu, irq_num);
|
||||
if (!vgic_dist_irq_soft_pend(vcpu, irq_num))
|
||||
if (!vgic_dist_irq_soft_pend(vcpu, irq_num)) {
|
||||
vgic_dist_irq_clear_pending(vcpu, irq_num);
|
||||
vgic_cpu_irq_clear(vcpu, irq_num);
|
||||
if (!compute_pending_for_cpu(vcpu))
|
||||
clear_bit(cpuid, dist->irq_pending_on_cpu);
|
||||
}
|
||||
}
|
||||
|
||||
ret = false;
|
||||
|
|
Loading…
Reference in New Issue