mirror of
https://github.com/torvalds/linux.git
synced 2026-06-03 03:53:37 +02:00
KVM: arm64: vgic-v3: Refactor GICv3 SGI generation
As we're about to change the way SGIs are sent, start by splitting out some of the basic functionnality: instead of intermingling the broadcast and non-broadcast cases with the actual SGI generation, perform the following cleanups: - move the SGI queuing into its own helper - split the broadcast code from the affinity-driven code - replace the mask/shift combinations with FIELD_GET() - fix the confusion between vcpu_id and vcpu when handling the broadcast case The result is much more readable, and paves the way for further optimisations. Tested-by: Joey Gouly <joey.gouly@arm.com> Tested-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com> Reviewed-by: Zenghui Yu <yuzenghui@huawei.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20230927090911.3355209-4-maz@kernel.org Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
This commit is contained in:
parent
d455d366c4
commit
f3f60a5653
|
|
@ -1052,6 +1052,38 @@ static int match_mpidr(u64 sgi_aff, u16 sgi_cpu_mask, struct kvm_vcpu *vcpu)
|
|||
((((reg) & ICC_SGI1R_AFFINITY_## level ##_MASK) \
|
||||
>> ICC_SGI1R_AFFINITY_## level ##_SHIFT) << MPIDR_LEVEL_SHIFT(level))
|
||||
|
||||
static void vgic_v3_queue_sgi(struct kvm_vcpu *vcpu, u32 sgi, bool allow_group1)
|
||||
{
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, sgi);
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
|
||||
/*
|
||||
* An access targeting Group0 SGIs can only generate
|
||||
* those, while an access targeting Group1 SGIs can
|
||||
* generate interrupts of either group.
|
||||
*/
|
||||
if (!irq->group || allow_group1) {
|
||||
if (!irq->hw) {
|
||||
irq->pending_latch = true;
|
||||
vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
|
||||
} else {
|
||||
/* HW SGI? Ask the GIC to inject it */
|
||||
int err;
|
||||
err = irq_set_irqchip_state(irq->host_irq,
|
||||
IRQCHIP_STATE_PENDING,
|
||||
true);
|
||||
WARN_RATELIMIT(err, "IRQ %d", irq->host_irq);
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
}
|
||||
} else {
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
}
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
/**
|
||||
* vgic_v3_dispatch_sgi - handle SGI requests from VCPUs
|
||||
* @vcpu: The VCPU requesting a SGI
|
||||
|
|
@ -1070,19 +1102,30 @@ void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg, bool allow_group1)
|
|||
{
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
struct kvm_vcpu *c_vcpu;
|
||||
u16 target_cpus;
|
||||
unsigned long target_cpus;
|
||||
u64 mpidr;
|
||||
int sgi;
|
||||
int vcpu_id = vcpu->vcpu_id;
|
||||
bool broadcast;
|
||||
unsigned long c, flags;
|
||||
u32 sgi;
|
||||
unsigned long c;
|
||||
|
||||
sgi = FIELD_GET(ICC_SGI1R_SGI_ID_MASK, reg);
|
||||
|
||||
/* Broadcast */
|
||||
if (unlikely(reg & BIT_ULL(ICC_SGI1R_IRQ_ROUTING_MODE_BIT))) {
|
||||
kvm_for_each_vcpu(c, c_vcpu, kvm) {
|
||||
/* Don't signal the calling VCPU */
|
||||
if (c_vcpu == vcpu)
|
||||
continue;
|
||||
|
||||
vgic_v3_queue_sgi(c_vcpu, sgi, allow_group1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sgi = (reg & ICC_SGI1R_SGI_ID_MASK) >> ICC_SGI1R_SGI_ID_SHIFT;
|
||||
broadcast = reg & BIT_ULL(ICC_SGI1R_IRQ_ROUTING_MODE_BIT);
|
||||
target_cpus = (reg & ICC_SGI1R_TARGET_LIST_MASK) >> ICC_SGI1R_TARGET_LIST_SHIFT;
|
||||
mpidr = SGI_AFFINITY_LEVEL(reg, 3);
|
||||
mpidr |= SGI_AFFINITY_LEVEL(reg, 2);
|
||||
mpidr |= SGI_AFFINITY_LEVEL(reg, 1);
|
||||
target_cpus = FIELD_GET(ICC_SGI1R_TARGET_LIST_MASK, reg);
|
||||
|
||||
/*
|
||||
* We iterate over all VCPUs to find the MPIDRs matching the request.
|
||||
|
|
@ -1091,54 +1134,19 @@ void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg, bool allow_group1)
|
|||
* VCPUs when most of the times we just signal a single VCPU.
|
||||
*/
|
||||
kvm_for_each_vcpu(c, c_vcpu, kvm) {
|
||||
struct vgic_irq *irq;
|
||||
int level0;
|
||||
|
||||
/* Exit early if we have dealt with all requested CPUs */
|
||||
if (!broadcast && target_cpus == 0)
|
||||
if (target_cpus == 0)
|
||||
break;
|
||||
|
||||
/* Don't signal the calling VCPU */
|
||||
if (broadcast && c == vcpu_id)
|
||||
level0 = match_mpidr(mpidr, target_cpus, c_vcpu);
|
||||
if (level0 == -1)
|
||||
continue;
|
||||
|
||||
if (!broadcast) {
|
||||
int level0;
|
||||
/* remove this matching VCPU from the mask */
|
||||
target_cpus &= ~BIT(level0);
|
||||
|
||||
level0 = match_mpidr(mpidr, target_cpus, c_vcpu);
|
||||
if (level0 == -1)
|
||||
continue;
|
||||
|
||||
/* remove this matching VCPU from the mask */
|
||||
target_cpus &= ~BIT(level0);
|
||||
}
|
||||
|
||||
irq = vgic_get_irq(vcpu->kvm, c_vcpu, sgi);
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
|
||||
/*
|
||||
* An access targeting Group0 SGIs can only generate
|
||||
* those, while an access targeting Group1 SGIs can
|
||||
* generate interrupts of either group.
|
||||
*/
|
||||
if (!irq->group || allow_group1) {
|
||||
if (!irq->hw) {
|
||||
irq->pending_latch = true;
|
||||
vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
|
||||
} else {
|
||||
/* HW SGI? Ask the GIC to inject it */
|
||||
int err;
|
||||
err = irq_set_irqchip_state(irq->host_irq,
|
||||
IRQCHIP_STATE_PENDING,
|
||||
true);
|
||||
WARN_RATELIMIT(err, "IRQ %d", irq->host_irq);
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
}
|
||||
} else {
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
}
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
vgic_v3_queue_sgi(c_vcpu, sgi, allow_group1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user