Commit 96806229 authored by Marc Zyngier's avatar Marc Zyngier

irqchip/gic-v4.1: Add support for VPENDBASER's Dirty+Valid signaling

When a vPE is made resident, the GIC starts parsing the virtual pending
table to deliver pending interrupts. This takes place asynchronously,
and can at times take a long while. Long enough that the vcpu enters
the guest and hits WFI before any interrupt has been signaled yet.
The vcpu then exits, blocks, and now gets a doorbell. Rince, repeat.

In order to avoid the above, a (optional on GICv4, mandatory on v4.1)
feature allows the GIC to feedback to the hypervisor whether it is
done parsing the VPT by clearing the GICR_VPENDBASER.Dirty bit.
The hypervisor can then wait until the GIC is ready before actually
running the vPE.

Plug the detection code as well as polling on vPE schedule. While
at it, tidy-up the kernel message that displays the GICv4 optional
features.
Reviewed-by: default avatarZenghui Yu <yuzenghui@huawei.com>
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parent 8f3d9f35
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/dma-iommu.h> #include <linux/dma-iommu.h>
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/log2.h> #include <linux/log2.h>
...@@ -3672,6 +3673,20 @@ static int its_vpe_set_affinity(struct irq_data *d, ...@@ -3672,6 +3673,20 @@ static int its_vpe_set_affinity(struct irq_data *d,
return IRQ_SET_MASK_OK_DONE; return IRQ_SET_MASK_OK_DONE;
} }
static void its_wait_vpt_parse_complete(void)
{
void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
u64 val;
if (!gic_rdists->has_vpend_valid_dirty)
return;
WARN_ON_ONCE(readq_relaxed_poll_timeout(vlpi_base + GICR_VPENDBASER,
val,
!(val & GICR_VPENDBASER_Dirty),
10, 500));
}
static void its_vpe_schedule(struct its_vpe *vpe) static void its_vpe_schedule(struct its_vpe *vpe)
{ {
void __iomem *vlpi_base = gic_data_rdist_vlpi_base(); void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
...@@ -3702,6 +3717,8 @@ static void its_vpe_schedule(struct its_vpe *vpe) ...@@ -3702,6 +3717,8 @@ static void its_vpe_schedule(struct its_vpe *vpe)
val |= vpe->idai ? GICR_VPENDBASER_IDAI : 0; val |= vpe->idai ? GICR_VPENDBASER_IDAI : 0;
val |= GICR_VPENDBASER_Valid; val |= GICR_VPENDBASER_Valid;
gicr_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER); gicr_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
its_wait_vpt_parse_complete();
} }
static void its_vpe_deschedule(struct its_vpe *vpe) static void its_vpe_deschedule(struct its_vpe *vpe)
...@@ -3910,6 +3927,8 @@ static void its_vpe_4_1_schedule(struct its_vpe *vpe, ...@@ -3910,6 +3927,8 @@ static void its_vpe_4_1_schedule(struct its_vpe *vpe,
val |= FIELD_PREP(GICR_VPENDBASER_4_1_VPEID, vpe->vpe_id); val |= FIELD_PREP(GICR_VPENDBASER_4_1_VPEID, vpe->vpe_id);
gicr_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER); gicr_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
its_wait_vpt_parse_complete();
} }
static void its_vpe_4_1_deschedule(struct its_vpe *vpe, static void its_vpe_4_1_deschedule(struct its_vpe *vpe,
......
...@@ -873,6 +873,7 @@ static int __gic_update_rdist_properties(struct redist_region *region, ...@@ -873,6 +873,7 @@ static int __gic_update_rdist_properties(struct redist_region *region,
gic_data.rdists.has_rvpeid &= !!(typer & GICR_TYPER_RVPEID); gic_data.rdists.has_rvpeid &= !!(typer & GICR_TYPER_RVPEID);
gic_data.rdists.has_direct_lpi &= (!!(typer & GICR_TYPER_DirectLPIS) | gic_data.rdists.has_direct_lpi &= (!!(typer & GICR_TYPER_DirectLPIS) |
gic_data.rdists.has_rvpeid); gic_data.rdists.has_rvpeid);
gic_data.rdists.has_vpend_valid_dirty &= !!(typer & GICR_TYPER_DIRTY);
/* Detect non-sensical configurations */ /* Detect non-sensical configurations */
if (WARN_ON_ONCE(gic_data.rdists.has_rvpeid && !gic_data.rdists.has_vlpis)) { if (WARN_ON_ONCE(gic_data.rdists.has_rvpeid && !gic_data.rdists.has_vlpis)) {
...@@ -893,10 +894,11 @@ static void gic_update_rdist_properties(void) ...@@ -893,10 +894,11 @@ static void gic_update_rdist_properties(void)
if (WARN_ON(gic_data.ppi_nr == UINT_MAX)) if (WARN_ON(gic_data.ppi_nr == UINT_MAX))
gic_data.ppi_nr = 0; gic_data.ppi_nr = 0;
pr_info("%d PPIs implemented\n", gic_data.ppi_nr); pr_info("%d PPIs implemented\n", gic_data.ppi_nr);
pr_info("%sVLPI support, %sdirect LPI support, %sRVPEID support\n", if (gic_data.rdists.has_vlpis)
!gic_data.rdists.has_vlpis ? "no " : "", pr_info("GICv4 features: %s%s%s\n",
!gic_data.rdists.has_direct_lpi ? "no " : "", gic_data.rdists.has_direct_lpi ? "DirectLPI " : "",
!gic_data.rdists.has_rvpeid ? "no " : ""); gic_data.rdists.has_rvpeid ? "RVPEID " : "",
gic_data.rdists.has_vpend_valid_dirty ? "Valid+Dirty " : "");
} }
/* Check whether it's single security state view */ /* Check whether it's single security state view */
...@@ -1620,6 +1622,7 @@ static int __init gic_init_bases(void __iomem *dist_base, ...@@ -1620,6 +1622,7 @@ static int __init gic_init_bases(void __iomem *dist_base,
gic_data.rdists.has_rvpeid = true; gic_data.rdists.has_rvpeid = true;
gic_data.rdists.has_vlpis = true; gic_data.rdists.has_vlpis = true;
gic_data.rdists.has_direct_lpi = true; gic_data.rdists.has_direct_lpi = true;
gic_data.rdists.has_vpend_valid_dirty = true;
if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdists.rdist)) { if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdists.rdist)) {
err = -ENOMEM; err = -ENOMEM;
......
...@@ -243,6 +243,7 @@ ...@@ -243,6 +243,7 @@
#define GICR_TYPER_PLPIS (1U << 0) #define GICR_TYPER_PLPIS (1U << 0)
#define GICR_TYPER_VLPIS (1U << 1) #define GICR_TYPER_VLPIS (1U << 1)
#define GICR_TYPER_DIRTY (1U << 2)
#define GICR_TYPER_DirectLPIS (1U << 3) #define GICR_TYPER_DirectLPIS (1U << 3)
#define GICR_TYPER_LAST (1U << 4) #define GICR_TYPER_LAST (1U << 4)
#define GICR_TYPER_RVPEID (1U << 7) #define GICR_TYPER_RVPEID (1U << 7)
...@@ -686,6 +687,7 @@ struct rdists { ...@@ -686,6 +687,7 @@ struct rdists {
bool has_vlpis; bool has_vlpis;
bool has_rvpeid; bool has_rvpeid;
bool has_direct_lpi; bool has_direct_lpi;
bool has_vpend_valid_dirty;
}; };
struct irq_domain; struct irq_domain;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment