Skip to content

Commit 4645d11

Browse files
author
Marc Zyngier
committed
KVM: arm64: vgic-v3: Implement MMIO-based LPI invalidation
Since GICv4.1, it has become legal for an implementation to advertise GICR_{INVLPIR,INVALLR,SYNCR} while having an ITS, allowing for a more efficient invalidation scheme (no guest command queue contention when multiple CPUs are generating invalidations). Provide the invalidation registers as a primitive to their ITS counterpart. Note that we don't advertise them to the guest yet (the architecture allows an implementation to do this). Signed-off-by: Marc Zyngier <maz@kernel.org> Reviewed-by: Oliver Upton <oupton@google.com> Link: https://lore.kernel.org/r/20220405182327.205520-4-maz@kernel.org
1 parent 9482846 commit 4645d11

4 files changed

Lines changed: 112 additions & 21 deletions

File tree

arch/arm64/kvm/vgic/vgic-its.c

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,6 +1272,11 @@ static int vgic_its_cmd_handle_clear(struct kvm *kvm, struct vgic_its *its,
12721272
return 0;
12731273
}
12741274

1275+
int vgic_its_inv_lpi(struct kvm *kvm, struct vgic_irq *irq)
1276+
{
1277+
return update_lpi_config(kvm, irq, NULL, true);
1278+
}
1279+
12751280
/*
12761281
* The INV command syncs the configuration bits from the memory table.
12771282
* Must be called with the its_lock mutex held.
@@ -1288,7 +1293,41 @@ static int vgic_its_cmd_handle_inv(struct kvm *kvm, struct vgic_its *its,
12881293
if (!ite)
12891294
return E_ITS_INV_UNMAPPED_INTERRUPT;
12901295

1291-
return update_lpi_config(kvm, ite->irq, NULL, true);
1296+
return vgic_its_inv_lpi(kvm, ite->irq);
1297+
}
1298+
1299+
/**
1300+
* vgic_its_invall - invalidate all LPIs targetting a given vcpu
1301+
* @vcpu: the vcpu for which the RD is targetted by an invalidation
1302+
*
1303+
* Contrary to the INVALL command, this targets a RD instead of a
1304+
* collection, and we don't need to hold the its_lock, since no ITS is
1305+
* involved here.
1306+
*/
1307+
int vgic_its_invall(struct kvm_vcpu *vcpu)
1308+
{
1309+
struct kvm *kvm = vcpu->kvm;
1310+
int irq_count, i = 0;
1311+
u32 *intids;
1312+
1313+
irq_count = vgic_copy_lpi_list(kvm, vcpu, &intids);
1314+
if (irq_count < 0)
1315+
return irq_count;
1316+
1317+
for (i = 0; i < irq_count; i++) {
1318+
struct vgic_irq *irq = vgic_get_irq(kvm, NULL, intids[i]);
1319+
if (!irq)
1320+
continue;
1321+
update_lpi_config(kvm, irq, vcpu, false);
1322+
vgic_put_irq(kvm, irq);
1323+
}
1324+
1325+
kfree(intids);
1326+
1327+
if (vcpu->arch.vgic_cpu.vgic_v3.its_vpe.its_vm)
1328+
its_invall_vpe(&vcpu->arch.vgic_cpu.vgic_v3.its_vpe);
1329+
1330+
return 0;
12921331
}
12931332

12941333
/*
@@ -1305,32 +1344,13 @@ static int vgic_its_cmd_handle_invall(struct kvm *kvm, struct vgic_its *its,
13051344
u32 coll_id = its_cmd_get_collection(its_cmd);
13061345
struct its_collection *collection;
13071346
struct kvm_vcpu *vcpu;
1308-
struct vgic_irq *irq;
1309-
u32 *intids;
1310-
int irq_count, i;
13111347

13121348
collection = find_collection(its, coll_id);
13131349
if (!its_is_collection_mapped(collection))
13141350
return E_ITS_INVALL_UNMAPPED_COLLECTION;
13151351

13161352
vcpu = kvm_get_vcpu(kvm, collection->target_addr);
1317-
1318-
irq_count = vgic_copy_lpi_list(kvm, vcpu, &intids);
1319-
if (irq_count < 0)
1320-
return irq_count;
1321-
1322-
for (i = 0; i < irq_count; i++) {
1323-
irq = vgic_get_irq(kvm, NULL, intids[i]);
1324-
if (!irq)
1325-
continue;
1326-
update_lpi_config(kvm, irq, vcpu, false);
1327-
vgic_put_irq(kvm, irq);
1328-
}
1329-
1330-
kfree(intids);
1331-
1332-
if (vcpu->arch.vgic_cpu.vgic_v3.its_vpe.its_vm)
1333-
its_invall_vpe(&vcpu->arch.vgic_cpu.vgic_v3.its_vpe);
1353+
vgic_its_invall(vcpu);
13341354

13351355
return 0;
13361356
}

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,63 @@ static void vgic_mmio_write_pendbase(struct kvm_vcpu *vcpu,
543543
pendbaser) != old_pendbaser);
544544
}
545545

546+
static unsigned long vgic_mmio_read_sync(struct kvm_vcpu *vcpu,
547+
gpa_t addr, unsigned int len)
548+
{
549+
return !!atomic_read(&vcpu->arch.vgic_cpu.syncr_busy);
550+
}
551+
552+
static void vgic_set_rdist_busy(struct kvm_vcpu *vcpu, bool busy)
553+
{
554+
if (busy) {
555+
atomic_inc(&vcpu->arch.vgic_cpu.syncr_busy);
556+
smp_mb__after_atomic();
557+
} else {
558+
smp_mb__before_atomic();
559+
atomic_dec(&vcpu->arch.vgic_cpu.syncr_busy);
560+
}
561+
}
562+
563+
static void vgic_mmio_write_invlpi(struct kvm_vcpu *vcpu,
564+
gpa_t addr, unsigned int len,
565+
unsigned long val)
566+
{
567+
struct vgic_irq *irq;
568+
569+
/*
570+
* If the guest wrote only to the upper 32bit part of the
571+
* register, drop the write on the floor, as it is only for
572+
* vPEs (which we don't support for obvious reasons).
573+
*
574+
* Also discard the access if LPIs are not enabled.
575+
*/
576+
if ((addr & 4) || !vgic_lpis_enabled(vcpu))
577+
return;
578+
579+
vgic_set_rdist_busy(vcpu, true);
580+
581+
irq = vgic_get_irq(vcpu->kvm, NULL, lower_32_bits(val));
582+
if (irq) {
583+
vgic_its_inv_lpi(vcpu->kvm, irq);
584+
vgic_put_irq(vcpu->kvm, irq);
585+
}
586+
587+
vgic_set_rdist_busy(vcpu, false);
588+
}
589+
590+
static void vgic_mmio_write_invall(struct kvm_vcpu *vcpu,
591+
gpa_t addr, unsigned int len,
592+
unsigned long val)
593+
{
594+
/* See vgic_mmio_write_invlpi() for the early return rationale */
595+
if ((addr & 4) || !vgic_lpis_enabled(vcpu))
596+
return;
597+
598+
vgic_set_rdist_busy(vcpu, true);
599+
vgic_its_invall(vcpu);
600+
vgic_set_rdist_busy(vcpu, false);
601+
}
602+
546603
/*
547604
* The GICv3 per-IRQ registers are split to control PPIs and SGIs in the
548605
* redistributors, while SPIs are covered by registers in the distributor
@@ -648,6 +705,15 @@ static const struct vgic_register_region vgic_v3_rd_registers[] = {
648705
REGISTER_DESC_WITH_LENGTH(GICR_PENDBASER,
649706
vgic_mmio_read_pendbase, vgic_mmio_write_pendbase, 8,
650707
VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
708+
REGISTER_DESC_WITH_LENGTH(GICR_INVLPIR,
709+
vgic_mmio_read_raz, vgic_mmio_write_invlpi, 8,
710+
VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
711+
REGISTER_DESC_WITH_LENGTH(GICR_INVALLR,
712+
vgic_mmio_read_raz, vgic_mmio_write_invall, 8,
713+
VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
714+
REGISTER_DESC_WITH_LENGTH(GICR_SYNCR,
715+
vgic_mmio_read_sync, vgic_mmio_write_wi, 4,
716+
VGIC_ACCESS_32bit),
651717
REGISTER_DESC_WITH_LENGTH(GICR_IDREGS,
652718
vgic_mmio_read_v3_idregs, vgic_mmio_write_wi, 48,
653719
VGIC_ACCESS_32bit),

arch/arm64/kvm/vgic/vgic.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,10 @@ void vgic_lpi_translation_cache_init(struct kvm *kvm);
318318
void vgic_lpi_translation_cache_destroy(struct kvm *kvm);
319319
void vgic_its_invalidate_cache(struct kvm *kvm);
320320

321+
/* GICv4.1 MMIO interface */
322+
int vgic_its_inv_lpi(struct kvm *kvm, struct vgic_irq *irq);
323+
int vgic_its_invall(struct kvm_vcpu *vcpu);
324+
321325
bool vgic_supports_direct_msis(struct kvm *kvm);
322326
int vgic_v4_init(struct kvm *kvm);
323327
void vgic_v4_teardown(struct kvm *kvm);

include/kvm/arm_vgic.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ struct vgic_cpu {
344344
struct vgic_io_device rd_iodev;
345345
struct vgic_redist_region *rdreg;
346346
u32 rdreg_index;
347+
atomic_t syncr_busy;
347348

348349
/* Contains the attributes and gpa of the LPI pending tables. */
349350
u64 pendbaser;

0 commit comments

Comments
 (0)