Skip to content

Commit

Permalink
Merge tag 'kvm-arm-for-v4.3-rc7' of git://git.kernel.org/pub/scm/linu…
Browse files Browse the repository at this point in the history
…x/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.
  • Loading branch information
Paolo Bonzini committed Oct 21, 2015
2 parents b10d92a + 0d99749 commit ad355e3
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 41 deletions.
1 change: 1 addition & 0 deletions arch/arm/kvm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion arch/arm/kvm/arm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
19 changes: 19 additions & 0 deletions virt/kvm/arm/arch_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
}

/**
Expand Down
95 changes: 55 additions & 40 deletions virt/kvm/arm/vgic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -1295,31 +1336,6 @@ static void __kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
*/
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)
Expand Down Expand Up @@ -1421,21 +1437,16 @@ 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,
&map->active);

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;
}
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit ad355e3

Please sign in to comment.