Skip to content

Commit

Permalink
irqchip/gic-v4: Wait for GICR_VPENDBASER.Dirty to clear before desche…
Browse files Browse the repository at this point in the history
…duling

The way KVM drives GICv4.{0,1} is as follows:
- vcpu_load() makes the VPE resident, instructing the RD to start
  scanning for interrupts
- just before entering the guest, we check that the RD has finished
  scanning and that we can start running the vcpu
- on preemption, we deschedule the VPE by making it invalid on
  the RD

However, we are preemptible between the first two steps. If it so
happens *and* that the RD was still scanning, we nonetheless write
to the GICR_VPENDBASER register while Dirty is set, and bad things
happen (we're in UNPRED land).

This affects both the 4.0 and 4.1 implementations.

Make sure Dirty is cleared before performing the deschedule,
meaning that its_clear_vpend_valid() becomes a sort of full VPE
residency barrier.

Reported-by: Jingyi Wang <[email protected]>
Tested-by: Nianyao Tang <[email protected]>
Signed-off-by: Marc Zyngier <[email protected]>
Fixes: 57e3ceb ("KVM: arm64: Delay the polling of the GICR_VPENDBASER.Dirty bit")
Link: https://lore.kernel.org/r/[email protected]
  • Loading branch information
Marc Zyngier committed Apr 5, 2022
1 parent 76ff614 commit af27e41
Showing 1 changed file with 19 additions and 9 deletions.
28 changes: 19 additions & 9 deletions drivers/irqchip/irq-gic-v3-its.c
Original file line number Diff line number Diff line change
Expand Up @@ -3011,18 +3011,12 @@ static int __init allocate_lpi_tables(void)
return 0;
}

static u64 its_clear_vpend_valid(void __iomem *vlpi_base, u64 clr, u64 set)
static u64 read_vpend_dirty_clear(void __iomem *vlpi_base)
{
u32 count = 1000000; /* 1s! */
bool clean;
u64 val;

val = gicr_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
val &= ~GICR_VPENDBASER_Valid;
val &= ~clr;
val |= set;
gicr_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);

do {
val = gicr_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
clean = !(val & GICR_VPENDBASER_Dirty);
Expand All @@ -3033,10 +3027,26 @@ static u64 its_clear_vpend_valid(void __iomem *vlpi_base, u64 clr, u64 set)
}
} while (!clean && count);

if (unlikely(val & GICR_VPENDBASER_Dirty)) {
if (unlikely(!clean))
pr_err_ratelimited("ITS virtual pending table not cleaning\n");

return val;
}

static u64 its_clear_vpend_valid(void __iomem *vlpi_base, u64 clr, u64 set)
{
u64 val;

/* Make sure we wait until the RD is done with the initial scan */
val = read_vpend_dirty_clear(vlpi_base);
val &= ~GICR_VPENDBASER_Valid;
val &= ~clr;
val |= set;
gicr_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);

val = read_vpend_dirty_clear(vlpi_base);
if (unlikely(val & GICR_VPENDBASER_Dirty))
val |= GICR_VPENDBASER_PendingLast;
}

return val;
}
Expand Down

0 comments on commit af27e41

Please sign in to comment.