mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-15 01:01:56 -04:00
s390: A bunch of fixes and optimizations for interrupt and time
handling.
PPC: Mostly bug fixes.
ARM: No big features, but many small fixes and prerequisites including:
- a number of fixes for the arch-timer
- introducing proper level-triggered semantics for the arch-timers
- a series of patches to synchronously halt a guest (prerequisite for
IRQ forwarding)
- some tracepoint improvements
- a tweak for the EL2 panic handlers
- some more VGIC cleanups getting rid of redundant state
x86: quite a few changes:
- support for VT-d posted interrupts (i.e. PCI devices can inject
interrupts directly into vCPUs). This introduces a new component (in
virt/lib/) that connects VFIO and KVM together. The same infrastructure
will be used for ARM interrupt forwarding as well.
- more Hyper-V features, though the main one Hyper-V synthetic interrupt
controller will have to wait for 4.5. These will let KVM expose Hyper-V
devices.
- nested virtualization now supports VPID (same as PCID but for vCPUs)
which makes it quite a bit faster
- for future hardware that supports NVDIMM, there is support for clflushopt,
clwb, pcommit
- support for "split irqchip", i.e. LAPIC in kernel + IOAPIC/PIC/PIT in
userspace, which reduces the attack surface of the hypervisor
- obligatory smattering of SMM fixes
- on the guest side, stable scheduler clock support was rewritten to not
require help from the hypervisor.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.22 (GNU/Linux)
iQEcBAABAgAGBQJWO2IQAAoJEL/70l94x66D/K0H/3AovAgYmJQToZlimsktMk6a
f2xhdIqfU5lIQQh5uNBCfL3o9o8H9Py1ym7aEw3fmztPHHJYc91oTatt2UEKhmEw
VtZHp/dFHt3hwaIdXmjRPEXiYctraKCyrhaUYdWmUYkoKi7lW5OL5h+S7frG2U6u
p/hFKnHRZfXHr6NSgIqvYkKqtnc+C0FWY696IZMzgCksOO8jB1xrxoSN3tANW3oJ
PDV+4og0fN/Fr1capJUFEc/fejREHneANvlKrLaa8ht0qJQutoczNADUiSFLcMPG
iHljXeDsv5eyjMtUuIL8+MPzcrIt/y4rY41ZPiKggxULrXc6H+JJL/e/zThZpXc=
=iv2z
-----END PGP SIGNATURE-----
Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
Pull KVM updates from Paolo Bonzini:
"First batch of KVM changes for 4.4.
s390:
A bunch of fixes and optimizations for interrupt and time handling.
PPC:
Mostly bug fixes.
ARM:
No big features, but many small fixes and prerequisites including:
- a number of fixes for the arch-timer
- introducing proper level-triggered semantics for the arch-timers
- a series of patches to synchronously halt a guest (prerequisite
for IRQ forwarding)
- some tracepoint improvements
- a tweak for the EL2 panic handlers
- some more VGIC cleanups getting rid of redundant state
x86:
Quite a few changes:
- support for VT-d posted interrupts (i.e. PCI devices can inject
interrupts directly into vCPUs). This introduces a new
component (in virt/lib/) that connects VFIO and KVM together.
The same infrastructure will be used for ARM interrupt
forwarding as well.
- more Hyper-V features, though the main one Hyper-V synthetic
interrupt controller will have to wait for 4.5. These will let
KVM expose Hyper-V devices.
- nested virtualization now supports VPID (same as PCID but for
vCPUs) which makes it quite a bit faster
- for future hardware that supports NVDIMM, there is support for
clflushopt, clwb, pcommit
- support for "split irqchip", i.e. LAPIC in kernel +
IOAPIC/PIC/PIT in userspace, which reduces the attack surface of
the hypervisor
- obligatory smattering of SMM fixes
- on the guest side, stable scheduler clock support was rewritten
to not require help from the hypervisor"
* tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm: (123 commits)
KVM: VMX: Fix commit which broke PML
KVM: x86: obey KVM_X86_QUIRK_CD_NW_CLEARED in kvm_set_cr0()
KVM: x86: allow RSM from 64-bit mode
KVM: VMX: fix SMEP and SMAP without EPT
KVM: x86: move kvm_set_irq_inatomic to legacy device assignment
KVM: device assignment: remove pointless #ifdefs
KVM: x86: merge kvm_arch_set_irq with kvm_set_msi_inatomic
KVM: x86: zero apic_arb_prio on reset
drivers/hv: share Hyper-V SynIC constants with userspace
KVM: x86: handle SMBASE as physical address in RSM
KVM: x86: add read_phys to x86_emulate_ops
KVM: x86: removing unused variable
KVM: don't pointlessly leave KVM_COMPAT=y in non-KVM configs
KVM: arm/arm64: Merge vgic_set_lr() and vgic_sync_lr_elrsr()
KVM: arm/arm64: Clean up vgic_retire_lr() and surroundings
KVM: arm/arm64: Optimize away redundant LR tracking
KVM: s390: use simple switch statement as multiplexer
KVM: s390: drop useless newline in debugging data
KVM: s390: SCA must not cross page boundaries
KVM: arm: Do not indent the arguments of DECLARE_BITMAP
...
This commit is contained in:
commit
933425fb00
89 changed files with 2956 additions and 1029 deletions
1
virt/Makefile
Normal file
1
virt/Makefile
Normal file
|
|
@ -0,0 +1 @@
|
|||
obj-y += lib/
|
||||
|
|
@ -46,4 +46,7 @@ config KVM_GENERIC_DIRTYLOG_READ_PROTECT
|
|||
|
||||
config KVM_COMPAT
|
||||
def_bool y
|
||||
depends on COMPAT && !S390
|
||||
depends on KVM && COMPAT && !S390
|
||||
|
||||
config HAVE_KVM_IRQ_BYPASS
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
#include <kvm/arm_vgic.h>
|
||||
#include <kvm/arm_arch_timer.h>
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
static struct timecounter *timecounter;
|
||||
static struct workqueue_struct *wqueue;
|
||||
static unsigned int host_vtimer_irq;
|
||||
|
|
@ -59,18 +61,6 @@ static void timer_disarm(struct arch_timer_cpu *timer)
|
|||
}
|
||||
}
|
||||
|
||||
static void kvm_timer_inject_irq(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int ret;
|
||||
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
|
||||
|
||||
kvm_vgic_set_phys_irq_active(timer->map, true);
|
||||
ret = kvm_vgic_inject_mapped_irq(vcpu->kvm, vcpu->vcpu_id,
|
||||
timer->map,
|
||||
timer->irq->level);
|
||||
WARN_ON(ret);
|
||||
}
|
||||
|
||||
static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = *(struct kvm_vcpu **)dev_id;
|
||||
|
|
@ -111,14 +101,20 @@ static enum hrtimer_restart kvm_timer_expire(struct hrtimer *hrt)
|
|||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static bool kvm_timer_irq_can_fire(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
|
||||
|
||||
return !(timer->cntv_ctl & ARCH_TIMER_CTRL_IT_MASK) &&
|
||||
(timer->cntv_ctl & ARCH_TIMER_CTRL_ENABLE);
|
||||
}
|
||||
|
||||
bool kvm_timer_should_fire(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
|
||||
cycle_t cval, now;
|
||||
|
||||
if ((timer->cntv_ctl & ARCH_TIMER_CTRL_IT_MASK) ||
|
||||
!(timer->cntv_ctl & ARCH_TIMER_CTRL_ENABLE) ||
|
||||
kvm_vgic_get_phys_irq_active(timer->map))
|
||||
if (!kvm_timer_irq_can_fire(vcpu))
|
||||
return false;
|
||||
|
||||
cval = timer->cntv_cval;
|
||||
|
|
@ -127,12 +123,94 @@ bool kvm_timer_should_fire(struct kvm_vcpu *vcpu)
|
|||
return cval <= now;
|
||||
}
|
||||
|
||||
static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level)
|
||||
{
|
||||
int ret;
|
||||
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
|
||||
|
||||
BUG_ON(!vgic_initialized(vcpu->kvm));
|
||||
|
||||
timer->irq.level = new_level;
|
||||
trace_kvm_timer_update_irq(vcpu->vcpu_id, timer->map->virt_irq,
|
||||
timer->irq.level);
|
||||
ret = kvm_vgic_inject_mapped_irq(vcpu->kvm, vcpu->vcpu_id,
|
||||
timer->map,
|
||||
timer->irq.level);
|
||||
WARN_ON(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if there was a change in the timer state (should we raise or lower
|
||||
* the line level to the GIC).
|
||||
*/
|
||||
static void kvm_timer_update_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
|
||||
|
||||
/*
|
||||
* If userspace modified the timer registers via SET_ONE_REG before
|
||||
* the vgic was initialized, we mustn't set the timer->irq.level value
|
||||
* because the guest would never see the interrupt. Instead wait
|
||||
* until we call this function from kvm_timer_flush_hwstate.
|
||||
*/
|
||||
if (!vgic_initialized(vcpu->kvm))
|
||||
return;
|
||||
|
||||
if (kvm_timer_should_fire(vcpu) != timer->irq.level)
|
||||
kvm_timer_update_irq(vcpu, !timer->irq.level);
|
||||
}
|
||||
|
||||
/*
|
||||
* Schedule the background timer before calling kvm_vcpu_block, so that this
|
||||
* thread is removed from its waitqueue and made runnable when there's a timer
|
||||
* interrupt to handle.
|
||||
*/
|
||||
void kvm_timer_schedule(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
|
||||
u64 ns;
|
||||
cycle_t cval, now;
|
||||
|
||||
BUG_ON(timer_is_armed(timer));
|
||||
|
||||
/*
|
||||
* No need to schedule a background timer if the guest timer has
|
||||
* already expired, because kvm_vcpu_block will return before putting
|
||||
* the thread to sleep.
|
||||
*/
|
||||
if (kvm_timer_should_fire(vcpu))
|
||||
return;
|
||||
|
||||
/*
|
||||
* If the timer is not capable of raising interrupts (disabled or
|
||||
* masked), then there's no more work for us to do.
|
||||
*/
|
||||
if (!kvm_timer_irq_can_fire(vcpu))
|
||||
return;
|
||||
|
||||
/* The timer has not yet expired, schedule a background timer */
|
||||
cval = timer->cntv_cval;
|
||||
now = kvm_phys_timer_read() - vcpu->kvm->arch.timer.cntvoff;
|
||||
|
||||
ns = cyclecounter_cyc2ns(timecounter->cc,
|
||||
cval - now,
|
||||
timecounter->mask,
|
||||
&timecounter->frac);
|
||||
timer_arm(timer, ns);
|
||||
}
|
||||
|
||||
void kvm_timer_unschedule(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
|
||||
timer_disarm(timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_timer_flush_hwstate - prepare to move the virt timer to the cpu
|
||||
* @vcpu: The vcpu pointer
|
||||
*
|
||||
* Disarm any pending soft timers, since the world-switch code will write the
|
||||
* virtual timer state back to the physical CPU.
|
||||
* Check if the virtual timer has expired while we were running in the host,
|
||||
* and inject an interrupt if that was the case.
|
||||
*/
|
||||
void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
|
|
@ -140,28 +218,20 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
|
|||
bool phys_active;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* We're about to run this vcpu again, so there is no need to
|
||||
* keep the background timer running, as we're about to
|
||||
* populate the CPU timer again.
|
||||
*/
|
||||
timer_disarm(timer);
|
||||
kvm_timer_update_state(vcpu);
|
||||
|
||||
/*
|
||||
* If the timer expired while we were not scheduled, now is the time
|
||||
* to inject it.
|
||||
* If we enter the guest with the virtual input level to the VGIC
|
||||
* asserted, then we have already told the VGIC what we need to, and
|
||||
* we don't need to exit from the guest until the guest deactivates
|
||||
* the already injected interrupt, so therefore we should set the
|
||||
* hardware active state to prevent unnecessary exits from the guest.
|
||||
*
|
||||
* Conversely, if the virtual input level is deasserted, then always
|
||||
* clear the hardware active state to ensure that hardware interrupts
|
||||
* from the timer triggers a guest exit.
|
||||
*/
|
||||
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))
|
||||
if (timer->irq.level)
|
||||
phys_active = true;
|
||||
else
|
||||
phys_active = false;
|
||||
|
|
@ -176,32 +246,20 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
|
|||
* kvm_timer_sync_hwstate - sync timer state from cpu
|
||||
* @vcpu: The vcpu pointer
|
||||
*
|
||||
* Check if the virtual timer was armed and either schedule a corresponding
|
||||
* soft timer or inject directly if already expired.
|
||||
* Check if the virtual timer has expired while we were running in the guest,
|
||||
* and inject an interrupt if that was the case.
|
||||
*/
|
||||
void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
|
||||
cycle_t cval, now;
|
||||
u64 ns;
|
||||
|
||||
BUG_ON(timer_is_armed(timer));
|
||||
|
||||
if (kvm_timer_should_fire(vcpu)) {
|
||||
/*
|
||||
* Timer has already expired while we were not
|
||||
* looking. Inject the interrupt and carry on.
|
||||
*/
|
||||
kvm_timer_inject_irq(vcpu);
|
||||
return;
|
||||
}
|
||||
|
||||
cval = timer->cntv_cval;
|
||||
now = kvm_phys_timer_read() - vcpu->kvm->arch.timer.cntvoff;
|
||||
|
||||
ns = cyclecounter_cyc2ns(timecounter->cc, cval - now, timecounter->mask,
|
||||
&timecounter->frac);
|
||||
timer_arm(timer, ns);
|
||||
/*
|
||||
* The guest could have modified the timer registers or the timer
|
||||
* could have expired, update the timer state.
|
||||
*/
|
||||
kvm_timer_update_state(vcpu);
|
||||
}
|
||||
|
||||
int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu,
|
||||
|
|
@ -216,7 +274,7 @@ int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu,
|
|||
* kvm_vcpu_set_target(). To handle this, we determine
|
||||
* vcpu timer irq number when the vcpu is reset.
|
||||
*/
|
||||
timer->irq = irq;
|
||||
timer->irq.irq = irq->irq;
|
||||
|
||||
/*
|
||||
* The bits in CNTV_CTL are architecturally reset to UNKNOWN for ARMv8
|
||||
|
|
@ -225,6 +283,7 @@ int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu,
|
|||
* the ARMv7 architecture.
|
||||
*/
|
||||
timer->cntv_ctl = 0;
|
||||
kvm_timer_update_state(vcpu);
|
||||
|
||||
/*
|
||||
* Tell the VGIC that the virtual interrupt is tied to a
|
||||
|
|
@ -269,6 +328,8 @@ int kvm_arm_timer_set_reg(struct kvm_vcpu *vcpu, u64 regid, u64 value)
|
|||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
kvm_timer_update_state(vcpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
63
virt/kvm/arm/trace.h
Normal file
63
virt/kvm/arm/trace.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#if !defined(_TRACE_KVM_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_KVM_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM kvm
|
||||
|
||||
/*
|
||||
* Tracepoints for vgic
|
||||
*/
|
||||
TRACE_EVENT(vgic_update_irq_pending,
|
||||
TP_PROTO(unsigned long vcpu_id, __u32 irq, bool level),
|
||||
TP_ARGS(vcpu_id, irq, level),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, vcpu_id )
|
||||
__field( __u32, irq )
|
||||
__field( bool, level )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->vcpu_id = vcpu_id;
|
||||
__entry->irq = irq;
|
||||
__entry->level = level;
|
||||
),
|
||||
|
||||
TP_printk("VCPU: %ld, IRQ %d, level: %d",
|
||||
__entry->vcpu_id, __entry->irq, __entry->level)
|
||||
);
|
||||
|
||||
/*
|
||||
* Tracepoints for arch_timer
|
||||
*/
|
||||
TRACE_EVENT(kvm_timer_update_irq,
|
||||
TP_PROTO(unsigned long vcpu_id, __u32 irq, int level),
|
||||
TP_ARGS(vcpu_id, irq, level),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, vcpu_id )
|
||||
__field( __u32, irq )
|
||||
__field( int, level )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->vcpu_id = vcpu_id;
|
||||
__entry->irq = irq;
|
||||
__entry->level = level;
|
||||
),
|
||||
|
||||
TP_printk("VCPU: %ld, IRQ %d, level %d",
|
||||
__entry->vcpu_id, __entry->irq, __entry->level)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_KVM_H */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH ../../../virt/kvm/arm
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE trace
|
||||
|
||||
/* This part must be outside protection */
|
||||
#include <trace/define_trace.h>
|
||||
|
|
@ -79,11 +79,7 @@ static void vgic_v2_set_lr(struct kvm_vcpu *vcpu, int lr,
|
|||
lr_val |= (lr_desc.source << GICH_LR_PHYSID_CPUID_SHIFT);
|
||||
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr] = lr_val;
|
||||
}
|
||||
|
||||
static void vgic_v2_sync_lr_elrsr(struct kvm_vcpu *vcpu, int lr,
|
||||
struct vgic_lr lr_desc)
|
||||
{
|
||||
if (!(lr_desc.state & LR_STATE_MASK))
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr |= (1ULL << lr);
|
||||
else
|
||||
|
|
@ -158,6 +154,7 @@ static void vgic_v2_enable(struct kvm_vcpu *vcpu)
|
|||
* anyway.
|
||||
*/
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr = 0;
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr = ~0;
|
||||
|
||||
/* Get the show on the road... */
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr = GICH_HCR_EN;
|
||||
|
|
@ -166,7 +163,6 @@ static void vgic_v2_enable(struct kvm_vcpu *vcpu)
|
|||
static const struct vgic_ops vgic_v2_ops = {
|
||||
.get_lr = vgic_v2_get_lr,
|
||||
.set_lr = vgic_v2_set_lr,
|
||||
.sync_lr_elrsr = vgic_v2_sync_lr_elrsr,
|
||||
.get_elrsr = vgic_v2_get_elrsr,
|
||||
.get_eisr = vgic_v2_get_eisr,
|
||||
.clear_eisr = vgic_v2_clear_eisr,
|
||||
|
|
|
|||
|
|
@ -112,11 +112,7 @@ static void vgic_v3_set_lr(struct kvm_vcpu *vcpu, int lr,
|
|||
}
|
||||
|
||||
vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[LR_INDEX(lr)] = lr_val;
|
||||
}
|
||||
|
||||
static void vgic_v3_sync_lr_elrsr(struct kvm_vcpu *vcpu, int lr,
|
||||
struct vgic_lr lr_desc)
|
||||
{
|
||||
if (!(lr_desc.state & LR_STATE_MASK))
|
||||
vcpu->arch.vgic_cpu.vgic_v3.vgic_elrsr |= (1U << lr);
|
||||
else
|
||||
|
|
@ -193,6 +189,7 @@ static void vgic_v3_enable(struct kvm_vcpu *vcpu)
|
|||
* anyway.
|
||||
*/
|
||||
vgic_v3->vgic_vmcr = 0;
|
||||
vgic_v3->vgic_elrsr = ~0;
|
||||
|
||||
/*
|
||||
* If we are emulating a GICv3, we do it in an non-GICv2-compatible
|
||||
|
|
@ -211,7 +208,6 @@ static void vgic_v3_enable(struct kvm_vcpu *vcpu)
|
|||
static const struct vgic_ops vgic_v3_ops = {
|
||||
.get_lr = vgic_v3_get_lr,
|
||||
.set_lr = vgic_v3_set_lr,
|
||||
.sync_lr_elrsr = vgic_v3_sync_lr_elrsr,
|
||||
.get_elrsr = vgic_v3_get_elrsr,
|
||||
.get_eisr = vgic_v3_get_eisr,
|
||||
.clear_eisr = vgic_v3_clear_eisr,
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@
|
|||
#include <asm/kvm.h>
|
||||
#include <kvm/iodev.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace.h"
|
||||
|
||||
/*
|
||||
* How the whole thing works (courtesy of Christoffer Dall):
|
||||
*
|
||||
|
|
@ -102,11 +105,13 @@
|
|||
#include "vgic.h"
|
||||
|
||||
static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu);
|
||||
static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu);
|
||||
static void vgic_retire_lr(int lr_nr, struct kvm_vcpu *vcpu);
|
||||
static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr);
|
||||
static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr, struct vgic_lr lr_desc);
|
||||
static u64 vgic_get_elrsr(struct kvm_vcpu *vcpu);
|
||||
static struct irq_phys_map *vgic_irq_map_search(struct kvm_vcpu *vcpu,
|
||||
int virt_irq);
|
||||
static int compute_pending_for_cpu(struct kvm_vcpu *vcpu);
|
||||
|
||||
static const struct vgic_ops *vgic_ops;
|
||||
static const struct vgic_params *vgic;
|
||||
|
|
@ -357,6 +362,11 @@ static void vgic_dist_irq_clear_soft_pend(struct kvm_vcpu *vcpu, int irq)
|
|||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
||||
|
||||
vgic_bitmap_set_irq_val(&dist->irq_soft_pend, vcpu->vcpu_id, irq, 0);
|
||||
if (!vgic_dist_irq_get_level(vcpu, irq)) {
|
||||
vgic_dist_irq_clear_pending(vcpu, irq);
|
||||
if (!compute_pending_for_cpu(vcpu))
|
||||
clear_bit(vcpu->vcpu_id, dist->irq_pending_on_cpu);
|
||||
}
|
||||
}
|
||||
|
||||
static int vgic_dist_irq_is_pending(struct kvm_vcpu *vcpu, int irq)
|
||||
|
|
@ -531,34 +541,6 @@ 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)
|
||||
|
|
@ -589,7 +571,6 @@ 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;
|
||||
}
|
||||
|
|
@ -627,7 +608,6 @@ 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;
|
||||
}
|
||||
|
|
@ -684,10 +664,9 @@ bool vgic_handle_cfg_reg(u32 *reg, struct kvm_exit_mmio *mmio,
|
|||
vgic_reg_access(mmio, &val, offset,
|
||||
ACCESS_READ_VALUE | ACCESS_WRITE_VALUE);
|
||||
if (mmio->is_write) {
|
||||
if (offset < 8) {
|
||||
*reg = ~0U; /* Force PPIs/SGIs to 1 */
|
||||
/* Ignore writes to read-only SGI and PPI bits */
|
||||
if (offset < 8)
|
||||
return false;
|
||||
}
|
||||
|
||||
val = vgic_cfg_compress(val);
|
||||
if (offset & 4) {
|
||||
|
|
@ -713,9 +692,11 @@ bool vgic_handle_cfg_reg(u32 *reg, struct kvm_exit_mmio *mmio,
|
|||
void vgic_unqueue_irqs(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
u64 elrsr = vgic_get_elrsr(vcpu);
|
||||
unsigned long *elrsr_ptr = u64_to_bitmask(&elrsr);
|
||||
int i;
|
||||
|
||||
for_each_set_bit(i, vgic_cpu->lr_used, vgic_cpu->nr_lr) {
|
||||
for_each_clear_bit(i, elrsr_ptr, vgic_cpu->nr_lr) {
|
||||
struct vgic_lr lr = vgic_get_lr(vcpu, i);
|
||||
|
||||
/*
|
||||
|
|
@ -736,30 +717,14 @@ void vgic_unqueue_irqs(struct kvm_vcpu *vcpu)
|
|||
* interrupt then move the active state to the
|
||||
* distributor tracking bit.
|
||||
*/
|
||||
if (lr.state & LR_STATE_ACTIVE) {
|
||||
if (lr.state & LR_STATE_ACTIVE)
|
||||
vgic_irq_set_active(vcpu, lr.irq);
|
||||
lr.state &= ~LR_STATE_ACTIVE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reestablish the pending state on the distributor and the
|
||||
* CPU interface. It may have already been pending, but that
|
||||
* is fine, then we are only setting a few bits that were
|
||||
* already set.
|
||||
* CPU interface and mark the LR as free for other use.
|
||||
*/
|
||||
if (lr.state & LR_STATE_PENDING) {
|
||||
vgic_dist_irq_set_pending(vcpu, lr.irq);
|
||||
lr.state &= ~LR_STATE_PENDING;
|
||||
}
|
||||
|
||||
vgic_set_lr(vcpu, i, lr);
|
||||
|
||||
/*
|
||||
* Mark the LR as free for other use.
|
||||
*/
|
||||
BUG_ON(lr.state & LR_STATE_MASK);
|
||||
vgic_retire_lr(i, lr.irq, vcpu);
|
||||
vgic_irq_clear_queued(vcpu, lr.irq);
|
||||
vgic_retire_lr(i, vcpu);
|
||||
|
||||
/* Finally update the VGIC state. */
|
||||
vgic_update_state(vcpu->kvm);
|
||||
|
|
@ -1067,12 +1032,6 @@ static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr,
|
|||
vgic_ops->set_lr(vcpu, lr, vlr);
|
||||
}
|
||||
|
||||
static void vgic_sync_lr_elrsr(struct kvm_vcpu *vcpu, int lr,
|
||||
struct vgic_lr vlr)
|
||||
{
|
||||
vgic_ops->sync_lr_elrsr(vcpu, lr, vlr);
|
||||
}
|
||||
|
||||
static inline u64 vgic_get_elrsr(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vgic_ops->get_elrsr(vcpu);
|
||||
|
|
@ -1118,25 +1077,23 @@ static inline void vgic_enable(struct kvm_vcpu *vcpu)
|
|||
vgic_ops->enable(vcpu);
|
||||
}
|
||||
|
||||
static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu)
|
||||
static void vgic_retire_lr(int lr_nr, struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
struct vgic_lr vlr = vgic_get_lr(vcpu, lr_nr);
|
||||
|
||||
vgic_irq_clear_queued(vcpu, vlr.irq);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
vgic_dist_irq_set_pending(vcpu, vlr.irq);
|
||||
vlr.hwirq = 0;
|
||||
}
|
||||
|
||||
vlr.state = 0;
|
||||
vgic_set_lr(vcpu, lr_nr, vlr);
|
||||
clear_bit(lr_nr, vgic_cpu->lr_used);
|
||||
vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
|
||||
vgic_sync_lr_elrsr(vcpu, lr_nr, vlr);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1150,17 +1107,15 @@ static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu)
|
|||
*/
|
||||
static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
u64 elrsr = vgic_get_elrsr(vcpu);
|
||||
unsigned long *elrsr_ptr = u64_to_bitmask(&elrsr);
|
||||
int lr;
|
||||
|
||||
for_each_set_bit(lr, vgic_cpu->lr_used, vgic->nr_lr) {
|
||||
for_each_clear_bit(lr, elrsr_ptr, vgic->nr_lr) {
|
||||
struct vgic_lr vlr = vgic_get_lr(vcpu, lr);
|
||||
|
||||
if (!vgic_irq_is_enabled(vcpu, vlr.irq)) {
|
||||
vgic_retire_lr(lr, vlr.irq, vcpu);
|
||||
if (vgic_irq_is_queued(vcpu, vlr.irq))
|
||||
vgic_irq_clear_queued(vcpu, vlr.irq);
|
||||
}
|
||||
if (!vgic_irq_is_enabled(vcpu, vlr.irq))
|
||||
vgic_retire_lr(lr, vcpu);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1200,7 +1155,6 @@ static void vgic_queue_irq_to_lr(struct kvm_vcpu *vcpu, int irq,
|
|||
}
|
||||
|
||||
vgic_set_lr(vcpu, lr_nr, vlr);
|
||||
vgic_sync_lr_elrsr(vcpu, lr_nr, vlr);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1210,8 +1164,9 @@ static void vgic_queue_irq_to_lr(struct kvm_vcpu *vcpu, int irq,
|
|||
*/
|
||||
bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
||||
u64 elrsr = vgic_get_elrsr(vcpu);
|
||||
unsigned long *elrsr_ptr = u64_to_bitmask(&elrsr);
|
||||
struct vgic_lr vlr;
|
||||
int lr;
|
||||
|
||||
|
|
@ -1222,28 +1177,22 @@ bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
|
|||
|
||||
kvm_debug("Queue IRQ%d\n", irq);
|
||||
|
||||
lr = vgic_cpu->vgic_irq_lr_map[irq];
|
||||
|
||||
/* Do we have an active interrupt for the same CPUID? */
|
||||
if (lr != LR_EMPTY) {
|
||||
for_each_clear_bit(lr, elrsr_ptr, vgic->nr_lr) {
|
||||
vlr = vgic_get_lr(vcpu, lr);
|
||||
if (vlr.source == sgi_source_id) {
|
||||
if (vlr.irq == irq && vlr.source == sgi_source_id) {
|
||||
kvm_debug("LR%d piggyback for IRQ%d\n", lr, vlr.irq);
|
||||
BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
|
||||
vgic_queue_irq_to_lr(vcpu, irq, lr, vlr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to use another LR for this interrupt */
|
||||
lr = find_first_zero_bit((unsigned long *)vgic_cpu->lr_used,
|
||||
vgic->nr_lr);
|
||||
lr = find_first_bit(elrsr_ptr, vgic->nr_lr);
|
||||
if (lr >= vgic->nr_lr)
|
||||
return false;
|
||||
|
||||
kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id);
|
||||
vgic_cpu->vgic_irq_lr_map[irq] = lr;
|
||||
set_bit(lr, vgic_cpu->lr_used);
|
||||
|
||||
vlr.irq = irq;
|
||||
vlr.source = sgi_source_id;
|
||||
|
|
@ -1338,12 +1287,60 @@ epilog:
|
|||
}
|
||||
}
|
||||
|
||||
static int process_queued_irq(struct kvm_vcpu *vcpu,
|
||||
int lr, struct vgic_lr vlr)
|
||||
{
|
||||
int pending = 0;
|
||||
|
||||
/*
|
||||
* If the IRQ was EOIed (called from vgic_process_maintenance) or it
|
||||
* went from active to non-active (called from vgic_sync_hwirq) it was
|
||||
* also ACKed and we we therefore assume we can clear the soft pending
|
||||
* state (should it had been set) for this interrupt.
|
||||
*
|
||||
* Note: if the IRQ soft pending state was set after the IRQ was
|
||||
* acked, it actually shouldn't be cleared, but we have no way of
|
||||
* knowing that unless we start trapping ACKs when the soft-pending
|
||||
* state is set.
|
||||
*/
|
||||
vgic_dist_irq_clear_soft_pend(vcpu, vlr.irq);
|
||||
|
||||
/*
|
||||
* Tell the gic to start sampling this interrupt again.
|
||||
*/
|
||||
vgic_irq_clear_queued(vcpu, vlr.irq);
|
||||
|
||||
/* Any additional pending interrupt? */
|
||||
if (vgic_irq_is_edge(vcpu, vlr.irq)) {
|
||||
BUG_ON(!(vlr.state & LR_HW));
|
||||
pending = vgic_dist_irq_is_pending(vcpu, vlr.irq);
|
||||
} else {
|
||||
if (vgic_dist_irq_get_level(vcpu, vlr.irq)) {
|
||||
vgic_cpu_irq_set(vcpu, vlr.irq);
|
||||
pending = 1;
|
||||
} else {
|
||||
vgic_dist_irq_clear_pending(vcpu, vlr.irq);
|
||||
vgic_cpu_irq_clear(vcpu, vlr.irq);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Despite being EOIed, the LR may not have
|
||||
* been marked as empty.
|
||||
*/
|
||||
vlr.state = 0;
|
||||
vlr.hwirq = 0;
|
||||
vgic_set_lr(vcpu, lr, vlr);
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
static bool vgic_process_maintenance(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 status = vgic_get_interrupt_status(vcpu);
|
||||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
||||
bool level_pending = false;
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
int level_pending = 0;
|
||||
|
||||
kvm_debug("STATUS = %08x\n", status);
|
||||
|
||||
|
|
@ -1358,54 +1355,22 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu)
|
|||
|
||||
for_each_set_bit(lr, eisr_ptr, vgic->nr_lr) {
|
||||
struct vgic_lr vlr = vgic_get_lr(vcpu, lr);
|
||||
|
||||
WARN_ON(vgic_irq_is_edge(vcpu, vlr.irq));
|
||||
|
||||
spin_lock(&dist->lock);
|
||||
vgic_irq_clear_queued(vcpu, vlr.irq);
|
||||
WARN_ON(vlr.state & LR_STATE_MASK);
|
||||
vlr.state = 0;
|
||||
vgic_set_lr(vcpu, lr, vlr);
|
||||
|
||||
/*
|
||||
* If the IRQ was EOIed it was also ACKed and we we
|
||||
* therefore assume we can clear the soft pending
|
||||
* state (should it had been set) for this interrupt.
|
||||
*
|
||||
* Note: if the IRQ soft pending state was set after
|
||||
* the IRQ was acked, it actually shouldn't be
|
||||
* cleared, but we have no way of knowing that unless
|
||||
* we start trapping ACKs when the soft-pending state
|
||||
* is set.
|
||||
*/
|
||||
vgic_dist_irq_clear_soft_pend(vcpu, vlr.irq);
|
||||
|
||||
/*
|
||||
* kvm_notify_acked_irq calls kvm_set_irq()
|
||||
* to reset the IRQ level. Need to release the
|
||||
* lock for kvm_set_irq to grab it.
|
||||
* to reset the IRQ level, which grabs the dist->lock
|
||||
* so we call this before taking the dist->lock.
|
||||
*/
|
||||
spin_unlock(&dist->lock);
|
||||
|
||||
kvm_notify_acked_irq(kvm, 0,
|
||||
vlr.irq - VGIC_NR_PRIVATE_IRQS);
|
||||
|
||||
spin_lock(&dist->lock);
|
||||
|
||||
/* Any additional pending interrupt? */
|
||||
if (vgic_dist_irq_get_level(vcpu, vlr.irq)) {
|
||||
vgic_cpu_irq_set(vcpu, vlr.irq);
|
||||
level_pending = true;
|
||||
} else {
|
||||
vgic_dist_irq_clear_pending(vcpu, vlr.irq);
|
||||
vgic_cpu_irq_clear(vcpu, vlr.irq);
|
||||
}
|
||||
|
||||
level_pending |= process_queued_irq(vcpu, lr, vlr);
|
||||
spin_unlock(&dist->lock);
|
||||
|
||||
/*
|
||||
* Despite being EOIed, the LR may not have
|
||||
* been marked as empty.
|
||||
*/
|
||||
vgic_sync_lr_elrsr(vcpu, lr, vlr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1426,35 +1391,40 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu)
|
|||
/*
|
||||
* Save the physical active state, and reset it to inactive.
|
||||
*
|
||||
* Return 1 if HW interrupt went from active to inactive, and 0 otherwise.
|
||||
* Return true if there's a pending forwarded interrupt to queue.
|
||||
*/
|
||||
static int vgic_sync_hwirq(struct kvm_vcpu *vcpu, struct vgic_lr vlr)
|
||||
static bool vgic_sync_hwirq(struct kvm_vcpu *vcpu, int lr, struct vgic_lr vlr)
|
||||
{
|
||||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
||||
struct irq_phys_map *map;
|
||||
bool phys_active;
|
||||
bool level_pending;
|
||||
int ret;
|
||||
|
||||
if (!(vlr.state & LR_HW))
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
map = vgic_irq_map_search(vcpu, vlr.irq);
|
||||
BUG_ON(!map);
|
||||
|
||||
ret = irq_get_irqchip_state(map->irq,
|
||||
IRQCHIP_STATE_ACTIVE,
|
||||
&map->active);
|
||||
&phys_active);
|
||||
|
||||
WARN_ON(ret);
|
||||
|
||||
if (map->active)
|
||||
if (phys_active)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
spin_lock(&dist->lock);
|
||||
level_pending = process_queued_irq(vcpu, lr, vlr);
|
||||
spin_unlock(&dist->lock);
|
||||
return level_pending;
|
||||
}
|
||||
|
||||
/* Sync back the VGIC state after a guest run */
|
||||
static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
||||
u64 elrsr;
|
||||
unsigned long *elrsr_ptr;
|
||||
|
|
@ -1462,40 +1432,18 @@ static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
|
|||
bool level_pending;
|
||||
|
||||
level_pending = vgic_process_maintenance(vcpu);
|
||||
elrsr = vgic_get_elrsr(vcpu);
|
||||
elrsr_ptr = u64_to_bitmask(&elrsr);
|
||||
|
||||
/* Deal with HW interrupts, and clear mappings for empty LRs */
|
||||
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 (vgic_sync_hwirq(vcpu, vlr)) {
|
||||
/*
|
||||
* So this is a HW interrupt that the guest
|
||||
* EOI-ed. Clean the LR state and allow the
|
||||
* interrupt to be sampled again.
|
||||
*/
|
||||
vlr.state = 0;
|
||||
vlr.hwirq = 0;
|
||||
vgic_set_lr(vcpu, lr, vlr);
|
||||
vgic_irq_clear_queued(vcpu, vlr.irq);
|
||||
set_bit(lr, elrsr_ptr);
|
||||
}
|
||||
|
||||
if (!test_bit(lr, elrsr_ptr))
|
||||
continue;
|
||||
|
||||
clear_bit(lr, vgic_cpu->lr_used);
|
||||
struct vgic_lr vlr = vgic_get_lr(vcpu, lr);
|
||||
|
||||
level_pending |= vgic_sync_hwirq(vcpu, lr, vlr);
|
||||
BUG_ON(vlr.irq >= dist->nr_irqs);
|
||||
vgic_cpu->vgic_irq_lr_map[vlr.irq] = LR_EMPTY;
|
||||
}
|
||||
|
||||
/* Check if we still have something up our sleeve... */
|
||||
elrsr = vgic_get_elrsr(vcpu);
|
||||
elrsr_ptr = u64_to_bitmask(&elrsr);
|
||||
pending = find_first_zero_bit(elrsr_ptr, vgic->nr_lr);
|
||||
if (level_pending || pending < vgic->nr_lr)
|
||||
set_bit(vcpu->vcpu_id, dist->irq_pending_on_cpu);
|
||||
|
|
@ -1585,6 +1533,8 @@ static int vgic_update_irq_pending(struct kvm *kvm, int cpuid,
|
|||
int enabled;
|
||||
bool ret = true, can_inject = true;
|
||||
|
||||
trace_vgic_update_irq_pending(cpuid, irq_num, level);
|
||||
|
||||
if (irq_num >= min(kvm->arch.vgic.nr_irqs, 1020))
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -1863,30 +1813,6 @@ static void vgic_free_phys_irq_map_rcu(struct rcu_head *rcu)
|
|||
kfree(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_vgic_get_phys_irq_active - Return the active state of a mapped IRQ
|
||||
*
|
||||
* Return the logical active state of a mapped interrupt. This doesn't
|
||||
* necessarily reflects the current HW state.
|
||||
*/
|
||||
bool kvm_vgic_get_phys_irq_active(struct irq_phys_map *map)
|
||||
{
|
||||
BUG_ON(!map);
|
||||
return map->active;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_vgic_set_phys_irq_active - Set the active state of a mapped IRQ
|
||||
*
|
||||
* Set the logical active state of a mapped interrupt. This doesn't
|
||||
* immediately affects the HW state.
|
||||
*/
|
||||
void kvm_vgic_set_phys_irq_active(struct irq_phys_map *map, bool active)
|
||||
{
|
||||
BUG_ON(!map);
|
||||
map->active = active;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_vgic_unmap_phys_irq - Remove a virtual to physical IRQ mapping
|
||||
* @vcpu: The VCPU pointer
|
||||
|
|
@ -1942,12 +1868,10 @@ void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu)
|
|||
kfree(vgic_cpu->pending_shared);
|
||||
kfree(vgic_cpu->active_shared);
|
||||
kfree(vgic_cpu->pend_act_shared);
|
||||
kfree(vgic_cpu->vgic_irq_lr_map);
|
||||
vgic_destroy_irq_phys_map(vcpu->kvm, &vgic_cpu->irq_phys_map_list);
|
||||
vgic_cpu->pending_shared = NULL;
|
||||
vgic_cpu->active_shared = NULL;
|
||||
vgic_cpu->pend_act_shared = NULL;
|
||||
vgic_cpu->vgic_irq_lr_map = NULL;
|
||||
}
|
||||
|
||||
static int vgic_vcpu_init_maps(struct kvm_vcpu *vcpu, int nr_irqs)
|
||||
|
|
@ -1958,18 +1882,14 @@ static int vgic_vcpu_init_maps(struct kvm_vcpu *vcpu, int nr_irqs)
|
|||
vgic_cpu->pending_shared = kzalloc(sz, GFP_KERNEL);
|
||||
vgic_cpu->active_shared = kzalloc(sz, GFP_KERNEL);
|
||||
vgic_cpu->pend_act_shared = kzalloc(sz, GFP_KERNEL);
|
||||
vgic_cpu->vgic_irq_lr_map = kmalloc(nr_irqs, GFP_KERNEL);
|
||||
|
||||
if (!vgic_cpu->pending_shared
|
||||
|| !vgic_cpu->active_shared
|
||||
|| !vgic_cpu->pend_act_shared
|
||||
|| !vgic_cpu->vgic_irq_lr_map) {
|
||||
|| !vgic_cpu->pend_act_shared) {
|
||||
kvm_vgic_vcpu_destroy(vcpu);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(vgic_cpu->vgic_irq_lr_map, LR_EMPTY, nr_irqs);
|
||||
|
||||
/*
|
||||
* Store the number of LRs per vcpu, so we don't have to go
|
||||
* all the way to the distributor structure to find out. Only
|
||||
|
|
@ -2111,14 +2031,24 @@ int vgic_init(struct kvm *kvm)
|
|||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < dist->nr_irqs; i++) {
|
||||
if (i < VGIC_NR_PPIS)
|
||||
/*
|
||||
* Enable and configure all SGIs to be edge-triggere and
|
||||
* configure all PPIs as level-triggered.
|
||||
*/
|
||||
for (i = 0; i < VGIC_NR_PRIVATE_IRQS; i++) {
|
||||
if (i < VGIC_NR_SGIS) {
|
||||
/* SGIs */
|
||||
vgic_bitmap_set_irq_val(&dist->irq_enabled,
|
||||
vcpu->vcpu_id, i, 1);
|
||||
if (i < VGIC_NR_PRIVATE_IRQS)
|
||||
vgic_bitmap_set_irq_val(&dist->irq_cfg,
|
||||
vcpu->vcpu_id, i,
|
||||
VGIC_CFG_EDGE);
|
||||
} else if (i < VGIC_NR_PRIVATE_IRQS) {
|
||||
/* PPIs */
|
||||
vgic_bitmap_set_irq_val(&dist->irq_cfg,
|
||||
vcpu->vcpu_id, i,
|
||||
VGIC_CFG_LEVEL);
|
||||
}
|
||||
}
|
||||
|
||||
vgic_enable(vcpu);
|
||||
|
|
|
|||
|
|
@ -94,6 +94,10 @@ static void async_pf_execute(struct work_struct *work)
|
|||
|
||||
trace_kvm_async_pf_completed(addr, gva);
|
||||
|
||||
/*
|
||||
* This memory barrier pairs with prepare_to_wait's set_current_state()
|
||||
*/
|
||||
smp_mb();
|
||||
if (waitqueue_active(&vcpu->wq))
|
||||
wake_up_interruptible(&vcpu->wq);
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/kvm_irqfd.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/wait.h>
|
||||
|
|
@ -34,73 +35,20 @@
|
|||
#include <linux/srcu.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/seqlock.h>
|
||||
#include <linux/irqbypass.h>
|
||||
#include <trace/events/kvm.h>
|
||||
|
||||
#include <kvm/iodev.h>
|
||||
|
||||
#ifdef CONFIG_HAVE_KVM_IRQFD
|
||||
/*
|
||||
* --------------------------------------------------------------------
|
||||
* irqfd: Allows an fd to be used to inject an interrupt to the guest
|
||||
*
|
||||
* Credit goes to Avi Kivity for the original idea.
|
||||
* --------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* Resampling irqfds are a special variety of irqfds used to emulate
|
||||
* level triggered interrupts. The interrupt is asserted on eventfd
|
||||
* trigger. On acknowledgement through the irq ack notifier, the
|
||||
* interrupt is de-asserted and userspace is notified through the
|
||||
* resamplefd. All resamplers on the same gsi are de-asserted
|
||||
* together, so we don't need to track the state of each individual
|
||||
* user. We can also therefore share the same irq source ID.
|
||||
*/
|
||||
struct _irqfd_resampler {
|
||||
struct kvm *kvm;
|
||||
/*
|
||||
* List of resampling struct _irqfd objects sharing this gsi.
|
||||
* RCU list modified under kvm->irqfds.resampler_lock
|
||||
*/
|
||||
struct list_head list;
|
||||
struct kvm_irq_ack_notifier notifier;
|
||||
/*
|
||||
* Entry in list of kvm->irqfd.resampler_list. Use for sharing
|
||||
* resamplers among irqfds on the same gsi.
|
||||
* Accessed and modified under kvm->irqfds.resampler_lock
|
||||
*/
|
||||
struct list_head link;
|
||||
};
|
||||
|
||||
struct _irqfd {
|
||||
/* Used for MSI fast-path */
|
||||
struct kvm *kvm;
|
||||
wait_queue_t wait;
|
||||
/* Update side is protected by irqfds.lock */
|
||||
struct kvm_kernel_irq_routing_entry irq_entry;
|
||||
seqcount_t irq_entry_sc;
|
||||
/* Used for level IRQ fast-path */
|
||||
int gsi;
|
||||
struct work_struct inject;
|
||||
/* The resampler used by this irqfd (resampler-only) */
|
||||
struct _irqfd_resampler *resampler;
|
||||
/* Eventfd notified on resample (resampler-only) */
|
||||
struct eventfd_ctx *resamplefd;
|
||||
/* Entry in list of irqfds for a resampler (resampler-only) */
|
||||
struct list_head resampler_link;
|
||||
/* Used for setup/shutdown */
|
||||
struct eventfd_ctx *eventfd;
|
||||
struct list_head list;
|
||||
poll_table pt;
|
||||
struct work_struct shutdown;
|
||||
};
|
||||
|
||||
static struct workqueue_struct *irqfd_cleanup_wq;
|
||||
|
||||
static void
|
||||
irqfd_inject(struct work_struct *work)
|
||||
{
|
||||
struct _irqfd *irqfd = container_of(work, struct _irqfd, inject);
|
||||
struct kvm_kernel_irqfd *irqfd =
|
||||
container_of(work, struct kvm_kernel_irqfd, inject);
|
||||
struct kvm *kvm = irqfd->kvm;
|
||||
|
||||
if (!irqfd->resampler) {
|
||||
|
|
@ -121,12 +69,13 @@ irqfd_inject(struct work_struct *work)
|
|||
static void
|
||||
irqfd_resampler_ack(struct kvm_irq_ack_notifier *kian)
|
||||
{
|
||||
struct _irqfd_resampler *resampler;
|
||||
struct kvm_kernel_irqfd_resampler *resampler;
|
||||
struct kvm *kvm;
|
||||
struct _irqfd *irqfd;
|
||||
struct kvm_kernel_irqfd *irqfd;
|
||||
int idx;
|
||||
|
||||
resampler = container_of(kian, struct _irqfd_resampler, notifier);
|
||||
resampler = container_of(kian,
|
||||
struct kvm_kernel_irqfd_resampler, notifier);
|
||||
kvm = resampler->kvm;
|
||||
|
||||
kvm_set_irq(kvm, KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID,
|
||||
|
|
@ -141,9 +90,9 @@ irqfd_resampler_ack(struct kvm_irq_ack_notifier *kian)
|
|||
}
|
||||
|
||||
static void
|
||||
irqfd_resampler_shutdown(struct _irqfd *irqfd)
|
||||
irqfd_resampler_shutdown(struct kvm_kernel_irqfd *irqfd)
|
||||
{
|
||||
struct _irqfd_resampler *resampler = irqfd->resampler;
|
||||
struct kvm_kernel_irqfd_resampler *resampler = irqfd->resampler;
|
||||
struct kvm *kvm = resampler->kvm;
|
||||
|
||||
mutex_lock(&kvm->irqfds.resampler_lock);
|
||||
|
|
@ -168,7 +117,8 @@ irqfd_resampler_shutdown(struct _irqfd *irqfd)
|
|||
static void
|
||||
irqfd_shutdown(struct work_struct *work)
|
||||
{
|
||||
struct _irqfd *irqfd = container_of(work, struct _irqfd, shutdown);
|
||||
struct kvm_kernel_irqfd *irqfd =
|
||||
container_of(work, struct kvm_kernel_irqfd, shutdown);
|
||||
u64 cnt;
|
||||
|
||||
/*
|
||||
|
|
@ -191,6 +141,9 @@ irqfd_shutdown(struct work_struct *work)
|
|||
/*
|
||||
* It is now safe to release the object's resources
|
||||
*/
|
||||
#ifdef CONFIG_HAVE_KVM_IRQ_BYPASS
|
||||
irq_bypass_unregister_consumer(&irqfd->consumer);
|
||||
#endif
|
||||
eventfd_ctx_put(irqfd->eventfd);
|
||||
kfree(irqfd);
|
||||
}
|
||||
|
|
@ -198,7 +151,7 @@ irqfd_shutdown(struct work_struct *work)
|
|||
|
||||
/* assumes kvm->irqfds.lock is held */
|
||||
static bool
|
||||
irqfd_is_active(struct _irqfd *irqfd)
|
||||
irqfd_is_active(struct kvm_kernel_irqfd *irqfd)
|
||||
{
|
||||
return list_empty(&irqfd->list) ? false : true;
|
||||
}
|
||||
|
|
@ -209,7 +162,7 @@ irqfd_is_active(struct _irqfd *irqfd)
|
|||
* assumes kvm->irqfds.lock is held
|
||||
*/
|
||||
static void
|
||||
irqfd_deactivate(struct _irqfd *irqfd)
|
||||
irqfd_deactivate(struct kvm_kernel_irqfd *irqfd)
|
||||
{
|
||||
BUG_ON(!irqfd_is_active(irqfd));
|
||||
|
||||
|
|
@ -218,13 +171,23 @@ irqfd_deactivate(struct _irqfd *irqfd)
|
|||
queue_work(irqfd_cleanup_wq, &irqfd->shutdown);
|
||||
}
|
||||
|
||||
int __attribute__((weak)) kvm_arch_set_irq_inatomic(
|
||||
struct kvm_kernel_irq_routing_entry *irq,
|
||||
struct kvm *kvm, int irq_source_id,
|
||||
int level,
|
||||
bool line_status)
|
||||
{
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called with wqh->lock held and interrupts disabled
|
||||
*/
|
||||
static int
|
||||
irqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key)
|
||||
{
|
||||
struct _irqfd *irqfd = container_of(wait, struct _irqfd, wait);
|
||||
struct kvm_kernel_irqfd *irqfd =
|
||||
container_of(wait, struct kvm_kernel_irqfd, wait);
|
||||
unsigned long flags = (unsigned long)key;
|
||||
struct kvm_kernel_irq_routing_entry irq;
|
||||
struct kvm *kvm = irqfd->kvm;
|
||||
|
|
@ -238,10 +201,9 @@ irqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key)
|
|||
irq = irqfd->irq_entry;
|
||||
} while (read_seqcount_retry(&irqfd->irq_entry_sc, seq));
|
||||
/* An event has been signaled, inject an interrupt */
|
||||
if (irq.type == KVM_IRQ_ROUTING_MSI)
|
||||
kvm_set_msi(&irq, kvm, KVM_USERSPACE_IRQ_SOURCE_ID, 1,
|
||||
false);
|
||||
else
|
||||
if (kvm_arch_set_irq_inatomic(&irq, kvm,
|
||||
KVM_USERSPACE_IRQ_SOURCE_ID, 1,
|
||||
false) == -EWOULDBLOCK)
|
||||
schedule_work(&irqfd->inject);
|
||||
srcu_read_unlock(&kvm->irq_srcu, idx);
|
||||
}
|
||||
|
|
@ -274,37 +236,54 @@ static void
|
|||
irqfd_ptable_queue_proc(struct file *file, wait_queue_head_t *wqh,
|
||||
poll_table *pt)
|
||||
{
|
||||
struct _irqfd *irqfd = container_of(pt, struct _irqfd, pt);
|
||||
struct kvm_kernel_irqfd *irqfd =
|
||||
container_of(pt, struct kvm_kernel_irqfd, pt);
|
||||
add_wait_queue(wqh, &irqfd->wait);
|
||||
}
|
||||
|
||||
/* Must be called under irqfds.lock */
|
||||
static void irqfd_update(struct kvm *kvm, struct _irqfd *irqfd)
|
||||
static void irqfd_update(struct kvm *kvm, struct kvm_kernel_irqfd *irqfd)
|
||||
{
|
||||
struct kvm_kernel_irq_routing_entry *e;
|
||||
struct kvm_kernel_irq_routing_entry entries[KVM_NR_IRQCHIPS];
|
||||
int i, n_entries;
|
||||
int n_entries;
|
||||
|
||||
n_entries = kvm_irq_map_gsi(kvm, entries, irqfd->gsi);
|
||||
|
||||
write_seqcount_begin(&irqfd->irq_entry_sc);
|
||||
|
||||
irqfd->irq_entry.type = 0;
|
||||
|
||||
e = entries;
|
||||
for (i = 0; i < n_entries; ++i, ++e) {
|
||||
/* Only fast-path MSI. */
|
||||
if (e->type == KVM_IRQ_ROUTING_MSI)
|
||||
irqfd->irq_entry = *e;
|
||||
}
|
||||
if (n_entries == 1)
|
||||
irqfd->irq_entry = *e;
|
||||
else
|
||||
irqfd->irq_entry.type = 0;
|
||||
|
||||
write_seqcount_end(&irqfd->irq_entry_sc);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HAVE_KVM_IRQ_BYPASS
|
||||
void __attribute__((weak)) kvm_arch_irq_bypass_stop(
|
||||
struct irq_bypass_consumer *cons)
|
||||
{
|
||||
}
|
||||
|
||||
void __attribute__((weak)) kvm_arch_irq_bypass_start(
|
||||
struct irq_bypass_consumer *cons)
|
||||
{
|
||||
}
|
||||
|
||||
int __attribute__((weak)) kvm_arch_update_irqfd_routing(
|
||||
struct kvm *kvm, unsigned int host_irq,
|
||||
uint32_t guest_irq, bool set)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args)
|
||||
{
|
||||
struct _irqfd *irqfd, *tmp;
|
||||
struct kvm_kernel_irqfd *irqfd, *tmp;
|
||||
struct fd f;
|
||||
struct eventfd_ctx *eventfd = NULL, *resamplefd = NULL;
|
||||
int ret;
|
||||
|
|
@ -340,7 +319,7 @@ kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args)
|
|||
irqfd->eventfd = eventfd;
|
||||
|
||||
if (args->flags & KVM_IRQFD_FLAG_RESAMPLE) {
|
||||
struct _irqfd_resampler *resampler;
|
||||
struct kvm_kernel_irqfd_resampler *resampler;
|
||||
|
||||
resamplefd = eventfd_ctx_fdget(args->resamplefd);
|
||||
if (IS_ERR(resamplefd)) {
|
||||
|
|
@ -428,6 +407,17 @@ kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args)
|
|||
* we might race against the POLLHUP
|
||||
*/
|
||||
fdput(f);
|
||||
#ifdef CONFIG_HAVE_KVM_IRQ_BYPASS
|
||||
irqfd->consumer.token = (void *)irqfd->eventfd;
|
||||
irqfd->consumer.add_producer = kvm_arch_irq_bypass_add_producer;
|
||||
irqfd->consumer.del_producer = kvm_arch_irq_bypass_del_producer;
|
||||
irqfd->consumer.stop = kvm_arch_irq_bypass_stop;
|
||||
irqfd->consumer.start = kvm_arch_irq_bypass_start;
|
||||
ret = irq_bypass_register_consumer(&irqfd->consumer);
|
||||
if (ret)
|
||||
pr_info("irq bypass consumer (token %p) registration fails: %d\n",
|
||||
irqfd->consumer.token, ret);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
@ -469,9 +459,18 @@ bool kvm_irq_has_notifier(struct kvm *kvm, unsigned irqchip, unsigned pin)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_irq_has_notifier);
|
||||
|
||||
void kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin)
|
||||
void kvm_notify_acked_gsi(struct kvm *kvm, int gsi)
|
||||
{
|
||||
struct kvm_irq_ack_notifier *kian;
|
||||
|
||||
hlist_for_each_entry_rcu(kian, &kvm->irq_ack_notifier_list,
|
||||
link)
|
||||
if (kian->gsi == gsi)
|
||||
kian->irq_acked(kian);
|
||||
}
|
||||
|
||||
void kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin)
|
||||
{
|
||||
int gsi, idx;
|
||||
|
||||
trace_kvm_ack_irq(irqchip, pin);
|
||||
|
|
@ -479,10 +478,7 @@ void kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin)
|
|||
idx = srcu_read_lock(&kvm->irq_srcu);
|
||||
gsi = kvm_irq_map_chip_pin(kvm, irqchip, pin);
|
||||
if (gsi != -1)
|
||||
hlist_for_each_entry_rcu(kian, &kvm->irq_ack_notifier_list,
|
||||
link)
|
||||
if (kian->gsi == gsi)
|
||||
kian->irq_acked(kian);
|
||||
kvm_notify_acked_gsi(kvm, gsi);
|
||||
srcu_read_unlock(&kvm->irq_srcu, idx);
|
||||
}
|
||||
|
||||
|
|
@ -525,7 +521,7 @@ kvm_eventfd_init(struct kvm *kvm)
|
|||
static int
|
||||
kvm_irqfd_deassign(struct kvm *kvm, struct kvm_irqfd *args)
|
||||
{
|
||||
struct _irqfd *irqfd, *tmp;
|
||||
struct kvm_kernel_irqfd *irqfd, *tmp;
|
||||
struct eventfd_ctx *eventfd;
|
||||
|
||||
eventfd = eventfd_ctx_fdget(args->fd);
|
||||
|
|
@ -581,7 +577,7 @@ kvm_irqfd(struct kvm *kvm, struct kvm_irqfd *args)
|
|||
void
|
||||
kvm_irqfd_release(struct kvm *kvm)
|
||||
{
|
||||
struct _irqfd *irqfd, *tmp;
|
||||
struct kvm_kernel_irqfd *irqfd, *tmp;
|
||||
|
||||
spin_lock_irq(&kvm->irqfds.lock);
|
||||
|
||||
|
|
@ -604,13 +600,23 @@ kvm_irqfd_release(struct kvm *kvm)
|
|||
*/
|
||||
void kvm_irq_routing_update(struct kvm *kvm)
|
||||
{
|
||||
struct _irqfd *irqfd;
|
||||
struct kvm_kernel_irqfd *irqfd;
|
||||
|
||||
spin_lock_irq(&kvm->irqfds.lock);
|
||||
|
||||
list_for_each_entry(irqfd, &kvm->irqfds.items, list)
|
||||
list_for_each_entry(irqfd, &kvm->irqfds.items, list) {
|
||||
irqfd_update(kvm, irqfd);
|
||||
|
||||
#ifdef CONFIG_HAVE_KVM_IRQ_BYPASS
|
||||
if (irqfd->producer) {
|
||||
int ret = kvm_arch_update_irqfd_routing(
|
||||
irqfd->kvm, irqfd->producer->irq,
|
||||
irqfd->gsi, 1);
|
||||
WARN_ON(ret);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
spin_unlock_irq(&kvm->irqfds.lock);
|
||||
}
|
||||
|
||||
|
|
@ -914,9 +920,7 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
|
|||
return -EINVAL;
|
||||
|
||||
/* ioeventfd with no length can't be combined with DATAMATCH */
|
||||
if (!args->len &&
|
||||
args->flags & (KVM_IOEVENTFD_FLAG_PIO |
|
||||
KVM_IOEVENTFD_FLAG_DATAMATCH))
|
||||
if (!args->len && (args->flags & KVM_IOEVENTFD_FLAG_DATAMATCH))
|
||||
return -EINVAL;
|
||||
|
||||
ret = kvm_assign_ioeventfd_idx(kvm, bus_idx, args);
|
||||
|
|
|
|||
|
|
@ -31,16 +31,6 @@
|
|||
#include <trace/events/kvm.h>
|
||||
#include "irq.h"
|
||||
|
||||
struct kvm_irq_routing_table {
|
||||
int chip[KVM_NR_IRQCHIPS][KVM_IRQCHIP_NUM_PINS];
|
||||
u32 nr_rt_entries;
|
||||
/*
|
||||
* Array indexed by gsi. Each entry contains list of irq chips
|
||||
* the gsi is connected to.
|
||||
*/
|
||||
struct hlist_head map[0];
|
||||
};
|
||||
|
||||
int kvm_irq_map_gsi(struct kvm *kvm,
|
||||
struct kvm_kernel_irq_routing_entry *entries, int gsi)
|
||||
{
|
||||
|
|
@ -154,11 +144,11 @@ static int setup_routing_entry(struct kvm_irq_routing_table *rt,
|
|||
|
||||
/*
|
||||
* Do not allow GSI to be mapped to the same irqchip more than once.
|
||||
* Allow only one to one mapping between GSI and MSI.
|
||||
* Allow only one to one mapping between GSI and non-irqchip routing.
|
||||
*/
|
||||
hlist_for_each_entry(ei, &rt->map[ue->gsi], link)
|
||||
if (ei->type == KVM_IRQ_ROUTING_MSI ||
|
||||
ue->type == KVM_IRQ_ROUTING_MSI ||
|
||||
if (ei->type != KVM_IRQ_ROUTING_IRQCHIP ||
|
||||
ue->type != KVM_IRQ_ROUTING_IRQCHIP ||
|
||||
ue->u.irqchip.irqchip == ei->irqchip.irqchip)
|
||||
return r;
|
||||
|
||||
|
|
@ -231,6 +221,8 @@ int kvm_set_irq_routing(struct kvm *kvm,
|
|||
kvm_irq_routing_update(kvm);
|
||||
mutex_unlock(&kvm->irq_lock);
|
||||
|
||||
kvm_arch_irq_routing_update(kvm);
|
||||
|
||||
synchronize_srcu_expedited(&kvm->irq_srcu);
|
||||
|
||||
new = old;
|
||||
|
|
|
|||
|
|
@ -230,6 +230,9 @@ int kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id)
|
|||
init_waitqueue_head(&vcpu->wq);
|
||||
kvm_async_pf_vcpu_init(vcpu);
|
||||
|
||||
vcpu->pre_pcpu = -1;
|
||||
INIT_LIST_HEAD(&vcpu->blocked_vcpu_list);
|
||||
|
||||
page = alloc_page(GFP_KERNEL | __GFP_ZERO);
|
||||
if (!page) {
|
||||
r = -ENOMEM;
|
||||
|
|
@ -2018,6 +2021,8 @@ void kvm_vcpu_block(struct kvm_vcpu *vcpu)
|
|||
} while (single_task_running() && ktime_before(cur, stop));
|
||||
}
|
||||
|
||||
kvm_arch_vcpu_blocking(vcpu);
|
||||
|
||||
for (;;) {
|
||||
prepare_to_wait(&vcpu->wq, &wait, TASK_INTERRUPTIBLE);
|
||||
|
||||
|
|
@ -2031,6 +2036,7 @@ void kvm_vcpu_block(struct kvm_vcpu *vcpu)
|
|||
finish_wait(&vcpu->wq, &wait);
|
||||
cur = ktime_get();
|
||||
|
||||
kvm_arch_vcpu_unblocking(vcpu);
|
||||
out:
|
||||
block_ns = ktime_to_ns(cur) - ktime_to_ns(start);
|
||||
|
||||
|
|
@ -2718,6 +2724,7 @@ static long kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg)
|
|||
case KVM_CAP_IRQFD:
|
||||
case KVM_CAP_IRQFD_RESAMPLE:
|
||||
#endif
|
||||
case KVM_CAP_IOEVENTFD_ANY_LENGTH:
|
||||
case KVM_CAP_CHECK_EXTENSION_VM:
|
||||
return 1;
|
||||
#ifdef CONFIG_HAVE_KVM_IRQ_ROUTING
|
||||
|
|
@ -3341,7 +3348,7 @@ int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr,
|
|||
if (bus->dev_count - bus->ioeventfd_count > NR_IOBUS_DEVS - 1)
|
||||
return -ENOSPC;
|
||||
|
||||
new_bus = kzalloc(sizeof(*bus) + ((bus->dev_count + 1) *
|
||||
new_bus = kmalloc(sizeof(*bus) + ((bus->dev_count + 1) *
|
||||
sizeof(struct kvm_io_range)), GFP_KERNEL);
|
||||
if (!new_bus)
|
||||
return -ENOMEM;
|
||||
|
|
@ -3373,7 +3380,7 @@ int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
|
|||
if (r)
|
||||
return r;
|
||||
|
||||
new_bus = kzalloc(sizeof(*bus) + ((bus->dev_count - 1) *
|
||||
new_bus = kmalloc(sizeof(*bus) + ((bus->dev_count - 1) *
|
||||
sizeof(struct kvm_io_range)), GFP_KERNEL);
|
||||
if (!new_bus)
|
||||
return -ENOMEM;
|
||||
|
|
|
|||
2
virt/lib/Kconfig
Normal file
2
virt/lib/Kconfig
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
config IRQ_BYPASS_MANAGER
|
||||
tristate
|
||||
1
virt/lib/Makefile
Normal file
1
virt/lib/Makefile
Normal file
|
|
@ -0,0 +1 @@
|
|||
obj-$(CONFIG_IRQ_BYPASS_MANAGER) += irqbypass.o
|
||||
257
virt/lib/irqbypass.c
Normal file
257
virt/lib/irqbypass.c
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
* IRQ offload/bypass manager
|
||||
*
|
||||
* Copyright (C) 2015 Red Hat, Inc.
|
||||
* Copyright (c) 2015 Linaro Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Various virtualization hardware acceleration techniques allow bypassing or
|
||||
* offloading interrupts received from devices around the host kernel. Posted
|
||||
* Interrupts on Intel VT-d systems can allow interrupts to be received
|
||||
* directly by a virtual machine. ARM IRQ Forwarding allows forwarded physical
|
||||
* interrupts to be directly deactivated by the guest. This manager allows
|
||||
* interrupt producers and consumers to find each other to enable this sort of
|
||||
* bypass.
|
||||
*/
|
||||
|
||||
#include <linux/irqbypass.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("IRQ bypass manager utility module");
|
||||
|
||||
static LIST_HEAD(producers);
|
||||
static LIST_HEAD(consumers);
|
||||
static DEFINE_MUTEX(lock);
|
||||
|
||||
/* @lock must be held when calling connect */
|
||||
static int __connect(struct irq_bypass_producer *prod,
|
||||
struct irq_bypass_consumer *cons)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (prod->stop)
|
||||
prod->stop(prod);
|
||||
if (cons->stop)
|
||||
cons->stop(cons);
|
||||
|
||||
if (prod->add_consumer)
|
||||
ret = prod->add_consumer(prod, cons);
|
||||
|
||||
if (!ret) {
|
||||
ret = cons->add_producer(cons, prod);
|
||||
if (ret && prod->del_consumer)
|
||||
prod->del_consumer(prod, cons);
|
||||
}
|
||||
|
||||
if (cons->start)
|
||||
cons->start(cons);
|
||||
if (prod->start)
|
||||
prod->start(prod);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* @lock must be held when calling disconnect */
|
||||
static void __disconnect(struct irq_bypass_producer *prod,
|
||||
struct irq_bypass_consumer *cons)
|
||||
{
|
||||
if (prod->stop)
|
||||
prod->stop(prod);
|
||||
if (cons->stop)
|
||||
cons->stop(cons);
|
||||
|
||||
cons->del_producer(cons, prod);
|
||||
|
||||
if (prod->del_consumer)
|
||||
prod->del_consumer(prod, cons);
|
||||
|
||||
if (cons->start)
|
||||
cons->start(cons);
|
||||
if (prod->start)
|
||||
prod->start(prod);
|
||||
}
|
||||
|
||||
/**
|
||||
* irq_bypass_register_producer - register IRQ bypass producer
|
||||
* @producer: pointer to producer structure
|
||||
*
|
||||
* Add the provided IRQ producer to the list of producers and connect
|
||||
* with any matching token found on the IRQ consumers list.
|
||||
*/
|
||||
int irq_bypass_register_producer(struct irq_bypass_producer *producer)
|
||||
{
|
||||
struct irq_bypass_producer *tmp;
|
||||
struct irq_bypass_consumer *consumer;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&lock);
|
||||
|
||||
list_for_each_entry(tmp, &producers, node) {
|
||||
if (tmp->token == producer->token) {
|
||||
mutex_unlock(&lock);
|
||||
module_put(THIS_MODULE);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(consumer, &consumers, node) {
|
||||
if (consumer->token == producer->token) {
|
||||
int ret = __connect(producer, consumer);
|
||||
if (ret) {
|
||||
mutex_unlock(&lock);
|
||||
module_put(THIS_MODULE);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
list_add(&producer->node, &producers);
|
||||
|
||||
mutex_unlock(&lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_bypass_register_producer);
|
||||
|
||||
/**
|
||||
* irq_bypass_unregister_producer - unregister IRQ bypass producer
|
||||
* @producer: pointer to producer structure
|
||||
*
|
||||
* Remove a previously registered IRQ producer from the list of producers
|
||||
* and disconnect it from any connected IRQ consumer.
|
||||
*/
|
||||
void irq_bypass_unregister_producer(struct irq_bypass_producer *producer)
|
||||
{
|
||||
struct irq_bypass_producer *tmp;
|
||||
struct irq_bypass_consumer *consumer;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return; /* nothing in the list anyway */
|
||||
|
||||
mutex_lock(&lock);
|
||||
|
||||
list_for_each_entry(tmp, &producers, node) {
|
||||
if (tmp->token != producer->token)
|
||||
continue;
|
||||
|
||||
list_for_each_entry(consumer, &consumers, node) {
|
||||
if (consumer->token == producer->token) {
|
||||
__disconnect(producer, consumer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
list_del(&producer->node);
|
||||
module_put(THIS_MODULE);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&lock);
|
||||
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_bypass_unregister_producer);
|
||||
|
||||
/**
|
||||
* irq_bypass_register_consumer - register IRQ bypass consumer
|
||||
* @consumer: pointer to consumer structure
|
||||
*
|
||||
* Add the provided IRQ consumer to the list of consumers and connect
|
||||
* with any matching token found on the IRQ producer list.
|
||||
*/
|
||||
int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer)
|
||||
{
|
||||
struct irq_bypass_consumer *tmp;
|
||||
struct irq_bypass_producer *producer;
|
||||
|
||||
if (!consumer->add_producer || !consumer->del_producer)
|
||||
return -EINVAL;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&lock);
|
||||
|
||||
list_for_each_entry(tmp, &consumers, node) {
|
||||
if (tmp->token == consumer->token) {
|
||||
mutex_unlock(&lock);
|
||||
module_put(THIS_MODULE);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(producer, &producers, node) {
|
||||
if (producer->token == consumer->token) {
|
||||
int ret = __connect(producer, consumer);
|
||||
if (ret) {
|
||||
mutex_unlock(&lock);
|
||||
module_put(THIS_MODULE);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
list_add(&consumer->node, &consumers);
|
||||
|
||||
mutex_unlock(&lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_bypass_register_consumer);
|
||||
|
||||
/**
|
||||
* irq_bypass_unregister_consumer - unregister IRQ bypass consumer
|
||||
* @consumer: pointer to consumer structure
|
||||
*
|
||||
* Remove a previously registered IRQ consumer from the list of consumers
|
||||
* and disconnect it from any connected IRQ producer.
|
||||
*/
|
||||
void irq_bypass_unregister_consumer(struct irq_bypass_consumer *consumer)
|
||||
{
|
||||
struct irq_bypass_consumer *tmp;
|
||||
struct irq_bypass_producer *producer;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return; /* nothing in the list anyway */
|
||||
|
||||
mutex_lock(&lock);
|
||||
|
||||
list_for_each_entry(tmp, &consumers, node) {
|
||||
if (tmp->token != consumer->token)
|
||||
continue;
|
||||
|
||||
list_for_each_entry(producer, &producers, node) {
|
||||
if (producer->token == consumer->token) {
|
||||
__disconnect(producer, consumer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
list_del(&consumer->node);
|
||||
module_put(THIS_MODULE);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&lock);
|
||||
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_bypass_unregister_consumer);
|
||||
Loading…
Reference in a new issue