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: e643d8034036 ("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>

authored by

Heyi Guo and committed by
Marc Zyngier
6479450f 8208d170

+49 -17
+49 -17
drivers/irqchip/irq-gic-v3-its.c
··· 2059 return 0; 2060 } 2061 2062 static void its_cpu_init_lpis(void) 2063 { 2064 void __iomem *rbase = gic_data_rdist_rd_base(); ··· 2166 val = readl_relaxed(rbase + GICR_CTLR); 2167 val |= GICR_CTLR_ENABLE_LPIS; 2168 writel_relaxed(val, rbase + GICR_CTLR); 2169 2170 /* Make sure the GIC has seen the above */ 2171 dsb(sy); ··· 2802 static void its_vpe_deschedule(struct its_vpe *vpe) 2803 { 2804 void __iomem *vlpi_base = gic_data_rdist_vlpi_base(); 2805 - u32 count = 1000000; /* 1s! */ 2806 - bool clean; 2807 u64 val; 2808 2809 - /* We're being scheduled out */ 2810 - val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER); 2811 - val &= ~GICR_VPENDBASER_Valid; 2812 - gits_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER); 2813 2814 - do { 2815 - val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER); 2816 - clean = !(val & GICR_VPENDBASER_Dirty); 2817 - if (!clean) { 2818 - count--; 2819 - cpu_relax(); 2820 - udelay(1); 2821 - } 2822 - } while (!clean && count); 2823 - 2824 - if (unlikely(!clean && !count)) { 2825 pr_err_ratelimited("ITS virtual pending table not cleaning\n"); 2826 vpe->idai = false; 2827 vpe->pending_last = true;
··· 2059 return 0; 2060 } 2061 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 + 2085 static void its_cpu_init_lpis(void) 2086 { 2087 void __iomem *rbase = gic_data_rdist_rd_base(); ··· 2143 val = readl_relaxed(rbase + GICR_CTLR); 2144 val |= GICR_CTLR_ENABLE_LPIS; 2145 writel_relaxed(val, rbase + GICR_CTLR); 2146 + 2147 + if (gic_rdists->has_vlpis) { 2148 + void __iomem *vlpi_base = gic_data_rdist_vlpi_base(); 2149 + 2150 + /* 2151 + * It's possible for CPU to receive VLPIs before it is 2152 + * sheduled as a vPE, especially for the first CPU, and the 2153 + * VLPI with INTID larger than 2^(IDbits+1) will be considered 2154 + * as out of range and dropped by GIC. 2155 + * So we initialize IDbits to known value to avoid VLPI drop. 2156 + */ 2157 + val = (LPI_NRBITS - 1) & GICR_VPROPBASER_IDBITS_MASK; 2158 + pr_debug("GICv4: CPU%d: Init IDbits to 0x%llx for GICR_VPROPBASER\n", 2159 + smp_processor_id(), val); 2160 + gits_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER); 2161 + 2162 + /* 2163 + * Also clear Valid bit of GICR_VPENDBASER, in case some 2164 + * ancient programming gets left in and has possibility of 2165 + * corrupting memory. 2166 + */ 2167 + val = its_clear_vpend_valid(vlpi_base); 2168 + WARN_ON(val & GICR_VPENDBASER_Dirty); 2169 + } 2170 2171 /* Make sure the GIC has seen the above */ 2172 dsb(sy); ··· 2755 static void its_vpe_deschedule(struct its_vpe *vpe) 2756 { 2757 void __iomem *vlpi_base = gic_data_rdist_vlpi_base(); 2758 u64 val; 2759 2760 + val = its_clear_vpend_valid(vlpi_base); 2761 2762 + if (unlikely(val & GICR_VPENDBASER_Dirty)) { 2763 pr_err_ratelimited("ITS virtual pending table not cleaning\n"); 2764 vpe->idai = false; 2765 vpe->pending_last = true;