Skip to content

Commit b5daffb

Browse files
Marc Zyngieroupton
authored andcommitted
KVM: arm64: vgic-v3: Optimize affinity-based SGI injection
Our affinity-based SGI injection code is a bit daft. We iterate over all the CPUs trying to match the set of affinities that the guest is trying to reach, leading to some very bad behaviours if the selected targets are at a high vcpu index. Instead, we can now use the fact that we have an optimised MPIDR to vcpu mapping, and only look at the relevant values. This results in a much faster injection for large VMs, and in a near constant time, irrespective of the position in the vcpu index space. As a bonus, this is mostly deleting a lot of hard-to-read code. Nobody will complain about that. Suggested-by: Xu Zhao <zhaoxu.35@bytedance.com> 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-11-maz@kernel.org Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
1 parent 54a8006 commit b5daffb

File tree

1 file changed

+11
-53
lines changed

1 file changed

+11
-53
lines changed

arch/arm64/kvm/vgic/vgic-mmio-v3.c

Lines changed: 11 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,35 +1013,6 @@ int vgic_v3_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr)
10131013

10141014
return 0;
10151015
}
1016-
/*
1017-
* Compare a given affinity (level 1-3 and a level 0 mask, from the SGI
1018-
* generation register ICC_SGI1R_EL1) with a given VCPU.
1019-
* If the VCPU's MPIDR matches, return the level0 affinity, otherwise
1020-
* return -1.
1021-
*/
1022-
static int match_mpidr(u64 sgi_aff, u16 sgi_cpu_mask, struct kvm_vcpu *vcpu)
1023-
{
1024-
unsigned long affinity;
1025-
int level0;
1026-
1027-
/*
1028-
* Split the current VCPU's MPIDR into affinity level 0 and the
1029-
* rest as this is what we have to compare against.
1030-
*/
1031-
affinity = kvm_vcpu_get_mpidr_aff(vcpu);
1032-
level0 = MPIDR_AFFINITY_LEVEL(affinity, 0);
1033-
affinity &= ~MPIDR_LEVEL_MASK;
1034-
1035-
/* bail out if the upper three levels don't match */
1036-
if (sgi_aff != affinity)
1037-
return -1;
1038-
1039-
/* Is this VCPU's bit set in the mask ? */
1040-
if (!(sgi_cpu_mask & BIT(level0)))
1041-
return -1;
1042-
1043-
return level0;
1044-
}
10451016

10461017
/*
10471018
* The ICC_SGI* registers encode the affinity differently from the MPIDR,
@@ -1094,17 +1065,19 @@ static void vgic_v3_queue_sgi(struct kvm_vcpu *vcpu, u32 sgi, bool allow_group1)
10941065
* This will trap in sys_regs.c and call this function.
10951066
* This ICC_SGI1R_EL1 register contains the upper three affinity levels of the
10961067
* target processors as well as a bitmask of 16 Aff0 CPUs.
1097-
* If the interrupt routing mode bit is not set, we iterate over all VCPUs to
1098-
* check for matching ones. If this bit is set, we signal all, but not the
1099-
* calling VCPU.
1068+
*
1069+
* If the interrupt routing mode bit is not set, we iterate over the Aff0
1070+
* bits and signal the VCPUs matching the provided Aff{3,2,1}.
1071+
*
1072+
* If this bit is set, we signal all, but not the calling VCPU.
11001073
*/
11011074
void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg, bool allow_group1)
11021075
{
11031076
struct kvm *kvm = vcpu->kvm;
11041077
struct kvm_vcpu *c_vcpu;
11051078
unsigned long target_cpus;
11061079
u64 mpidr;
1107-
u32 sgi;
1080+
u32 sgi, aff0;
11081081
unsigned long c;
11091082

11101083
sgi = FIELD_GET(ICC_SGI1R_SGI_ID_MASK, reg);
@@ -1122,31 +1095,16 @@ void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg, bool allow_group1)
11221095
return;
11231096
}
11241097

1098+
/* We iterate over affinities to find the corresponding vcpus */
11251099
mpidr = SGI_AFFINITY_LEVEL(reg, 3);
11261100
mpidr |= SGI_AFFINITY_LEVEL(reg, 2);
11271101
mpidr |= SGI_AFFINITY_LEVEL(reg, 1);
11281102
target_cpus = FIELD_GET(ICC_SGI1R_TARGET_LIST_MASK, reg);
11291103

1130-
/*
1131-
* We iterate over all VCPUs to find the MPIDRs matching the request.
1132-
* If we have handled one CPU, we clear its bit to detect early
1133-
* if we are already finished. This avoids iterating through all
1134-
* VCPUs when most of the times we just signal a single VCPU.
1135-
*/
1136-
kvm_for_each_vcpu(c, c_vcpu, kvm) {
1137-
int level0;
1138-
1139-
/* Exit early if we have dealt with all requested CPUs */
1140-
if (target_cpus == 0)
1141-
break;
1142-
level0 = match_mpidr(mpidr, target_cpus, c_vcpu);
1143-
if (level0 == -1)
1144-
continue;
1145-
1146-
/* remove this matching VCPU from the mask */
1147-
target_cpus &= ~BIT(level0);
1148-
1149-
vgic_v3_queue_sgi(c_vcpu, sgi, allow_group1);
1104+
for_each_set_bit(aff0, &target_cpus, hweight_long(ICC_SGI1R_TARGET_LIST_MASK)) {
1105+
c_vcpu = kvm_mpidr_to_vcpu(kvm, mpidr | aff0);
1106+
if (c_vcpu)
1107+
vgic_v3_queue_sgi(c_vcpu, sgi, allow_group1);
11501108
}
11511109
}
11521110

0 commit comments

Comments
 (0)