Skip to content

Commit 6479450

Browse files
Heyi GuoMarc Zyngier
authored andcommitted
irqchip/gic-v4: Fix occasional VLPI drop
1. In current implementation, every VLPI will temporarily be mapped to the first CPU in system (normally CPU0) and then moved to the real scheduled CPU later. 2. So there is a time window and a VLPI may be sent to CPU0 instead of the real scheduled vCPU, in a multi-CPU virtual machine. 3. However, CPU0 may have not been scheduled as a virtual CPU after system boots up, so the value of its GICR_VPROPBASER is unknown at that moment. 4. If the INTID of VLPI is larger than 2^(GICR_VPROPBASER.IDbits+1), while IDbits is also in unknown state, GIC will behave as if the VLPI is out of range and simply drop it, which results in interrupt missing in Guest. As no code will clear GICR_VPROPBASER at runtime, we can safely initialize the IDbits field at boot time for each CPU to get rid of this issue. We also clear Valid bit of GICR_VPENDBASER in case any ancient programming gets left in and causes memory corrupting. A new function its_clear_vpend_valid() is added to reuse the code in its_vpe_deschedule(). Fixes: e643d80 ("irqchip/gic-v3-its: Add VPE scheduling") Signed-off-by: Heyi Guo <guoheyi@huawei.com> Signed-off-by: Heyi Guo <heyi.guo@linaro.org> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
1 parent 8208d17 commit 6479450

File tree

1 file changed

+49
-17
lines changed

1 file changed

+49
-17
lines changed

drivers/irqchip/irq-gic-v3-its.c

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2059,6 +2059,29 @@ static int __init allocate_lpi_tables(void)
20592059
return 0;
20602060
}
20612061

2062+
static u64 its_clear_vpend_valid(void __iomem *vlpi_base)
2063+
{
2064+
u32 count = 1000000; /* 1s! */
2065+
bool clean;
2066+
u64 val;
2067+
2068+
val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
2069+
val &= ~GICR_VPENDBASER_Valid;
2070+
gits_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
2071+
2072+
do {
2073+
val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
2074+
clean = !(val & GICR_VPENDBASER_Dirty);
2075+
if (!clean) {
2076+
count--;
2077+
cpu_relax();
2078+
udelay(1);
2079+
}
2080+
} while (!clean && count);
2081+
2082+
return val;
2083+
}
2084+
20622085
static void its_cpu_init_lpis(void)
20632086
{
20642087
void __iomem *rbase = gic_data_rdist_rd_base();
@@ -2144,6 +2167,30 @@ static void its_cpu_init_lpis(void)
21442167
val |= GICR_CTLR_ENABLE_LPIS;
21452168
writel_relaxed(val, rbase + GICR_CTLR);
21462169

2170+
if (gic_rdists->has_vlpis) {
2171+
void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
2172+
2173+
/*
2174+
* It's possible for CPU to receive VLPIs before it is
2175+
* sheduled as a vPE, especially for the first CPU, and the
2176+
* VLPI with INTID larger than 2^(IDbits+1) will be considered
2177+
* as out of range and dropped by GIC.
2178+
* So we initialize IDbits to known value to avoid VLPI drop.
2179+
*/
2180+
val = (LPI_NRBITS - 1) & GICR_VPROPBASER_IDBITS_MASK;
2181+
pr_debug("GICv4: CPU%d: Init IDbits to 0x%llx for GICR_VPROPBASER\n",
2182+
smp_processor_id(), val);
2183+
gits_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER);
2184+
2185+
/*
2186+
* Also clear Valid bit of GICR_VPENDBASER, in case some
2187+
* ancient programming gets left in and has possibility of
2188+
* corrupting memory.
2189+
*/
2190+
val = its_clear_vpend_valid(vlpi_base);
2191+
WARN_ON(val & GICR_VPENDBASER_Dirty);
2192+
}
2193+
21472194
/* Make sure the GIC has seen the above */
21482195
dsb(sy);
21492196
out:
@@ -2755,26 +2802,11 @@ static void its_vpe_schedule(struct its_vpe *vpe)
27552802
static void its_vpe_deschedule(struct its_vpe *vpe)
27562803
{
27572804
void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
2758-
u32 count = 1000000; /* 1s! */
2759-
bool clean;
27602805
u64 val;
27612806

2762-
/* We're being scheduled out */
2763-
val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
2764-
val &= ~GICR_VPENDBASER_Valid;
2765-
gits_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
2766-
2767-
do {
2768-
val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
2769-
clean = !(val & GICR_VPENDBASER_Dirty);
2770-
if (!clean) {
2771-
count--;
2772-
cpu_relax();
2773-
udelay(1);
2774-
}
2775-
} while (!clean && count);
2807+
val = its_clear_vpend_valid(vlpi_base);
27762808

2777-
if (unlikely(!clean && !count)) {
2809+
if (unlikely(val & GICR_VPENDBASER_Dirty)) {
27782810
pr_err_ratelimited("ITS virtual pending table not cleaning\n");
27792811
vpe->idai = false;
27802812
vpe->pending_last = true;

0 commit comments

Comments
 (0)