|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v9 05/28] ARM: GICv3: forward pending LPIs to guests
On Thu, 11 May 2017, Andre Przywara wrote:
> Upon receiving an LPI on the host, we need to find the right VCPU and
> virtual IRQ number to get this IRQ injected.
> Iterate our two-level LPI table to find this information quickly when
> the host takes an LPI. Call the existing injection function to let the
> GIC emulation deal with this interrupt.
> Also we enhance struct pending_irq to cache the pending bit and the
> priority information for LPIs.
I can see that you added "uint8_t lpi_priority" to pending_irq. Where
are we caching the pending bit?
Also, I don't think the priority changes need to be part of this patch;
without out I would give my reviewed-by.
> Reading the information from there is
> faster than accessing the property table from guest memory. Also it
> use some padding area, so does not require more memory.
> This introduces a do_LPI() as a hardware gic_ops and a function to
> retrieve the (cached) priority value of an LPI and a vgic_ops.
>
> Signed-off-by: Andre Przywara <andre.przywara@xxxxxxx>
> ---
> xen/arch/arm/gic-v2.c | 7 ++++
> xen/arch/arm/gic-v3-lpi.c | 71
> ++++++++++++++++++++++++++++++++++++++++
> xen/arch/arm/gic-v3.c | 1 +
> xen/arch/arm/gic.c | 8 ++++-
> xen/arch/arm/vgic-v2.c | 7 ++++
> xen/arch/arm/vgic-v3.c | 18 ++++++++++
> xen/arch/arm/vgic.c | 7 +++-
> xen/include/asm-arm/domain.h | 3 +-
> xen/include/asm-arm/gic.h | 2 ++
> xen/include/asm-arm/gic_v3_its.h | 8 +++++
> xen/include/asm-arm/vgic.h | 2 ++
> 11 files changed, 131 insertions(+), 3 deletions(-)
>
> diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c
> index 270a136..ffbe47c 100644
> --- a/xen/arch/arm/gic-v2.c
> +++ b/xen/arch/arm/gic-v2.c
> @@ -1217,6 +1217,12 @@ static int __init gicv2_init(void)
> return 0;
> }
>
> +static void gicv2_do_LPI(unsigned int lpi)
> +{
> + /* No LPIs in a GICv2 */
> + BUG();
> +}
> +
> const static struct gic_hw_operations gicv2_ops = {
> .info = &gicv2_info,
> .init = gicv2_init,
> @@ -1244,6 +1250,7 @@ const static struct gic_hw_operations gicv2_ops = {
> .make_hwdom_madt = gicv2_make_hwdom_madt,
> .map_hwdom_extra_mappings = gicv2_map_hwdown_extra_mappings,
> .iomem_deny_access = gicv2_iomem_deny_access,
> + .do_LPI = gicv2_do_LPI,
> };
>
> /* Set up the GIC */
> diff --git a/xen/arch/arm/gic-v3-lpi.c b/xen/arch/arm/gic-v3-lpi.c
> index 292f2d0..44f6315 100644
> --- a/xen/arch/arm/gic-v3-lpi.c
> +++ b/xen/arch/arm/gic-v3-lpi.c
> @@ -136,6 +136,77 @@ uint64_t gicv3_get_redist_address(unsigned int cpu, bool
> use_pta)
> return per_cpu(lpi_redist, cpu).redist_id << 16;
> }
>
> +/*
> + * Handle incoming LPIs, which are a bit special, because they are
> potentially
> + * numerous and also only get injected into guests. Treat them specially
> here,
> + * by just looking up their target vCPU and virtual LPI number and hand it
> + * over to the injection function.
> + * Please note that LPIs are edge-triggered only, also have no active state,
> + * so spurious interrupts on the host side are no issue (we can just ignore
> + * them).
> + * Also a guest cannot expect that firing interrupts that haven't been
> + * fully configured yet will reach the CPU, so we don't need to care about
> + * this special case.
> + */
> +void gicv3_do_LPI(unsigned int lpi)
> +{
> + struct domain *d;
> + union host_lpi *hlpip, hlpi;
> + struct vcpu *vcpu;
> +
> + irq_enter();
> +
> + /* EOI the LPI already. */
> + WRITE_SYSREG32(lpi, ICC_EOIR1_EL1);
> +
> + /* Find out if a guest mapped something to this physical LPI. */
> + hlpip = gic_get_host_lpi(lpi);
> + if ( !hlpip )
> + goto out;
> +
> + hlpi.data = read_u64_atomic(&hlpip->data);
> +
> + /*
> + * Unmapped events are marked with an invalid LPI ID. We can safely
> + * ignore them, as they have no further state and no-one can expect
> + * to see them if they have not been mapped.
> + */
> + if ( hlpi.virt_lpi == INVALID_LPI )
> + goto out;
> +
> + d = rcu_lock_domain_by_id(hlpi.dom_id);
> + if ( !d )
> + goto out;
> +
> + /* Make sure we don't step beyond the vcpu array. */
> + if ( hlpi.vcpu_id >= d->max_vcpus )
> + {
> + rcu_unlock_domain(d);
> + goto out;
> + }
> +
> + vcpu = d->vcpu[hlpi.vcpu_id];
> +
> + /* Check if the VCPU is ready to receive LPIs. */
> + if ( vcpu->arch.vgic.flags & VGIC_V3_LPIS_ENABLED )
> + /*
> + * TODO: Investigate what to do here for potential interrupt storms.
> + * As we keep all host LPIs enabled, for disabling LPIs we would need
> + * to queue a ITS host command, which we avoid so far during a
> guest's
> + * runtime. Also re-enabling would trigger a host command upon the
> + * guest sending a command, which could be an attack vector for
> + * hogging the host command queue.
> + * See the thread around here for some background:
> + *
> https://lists.xen.org/archives/html/xen-devel/2016-12/msg00003.html
> + */
> + vgic_vcpu_inject_irq(vcpu, hlpi.virt_lpi);
> +
> + rcu_unlock_domain(d);
> +
> +out:
> + irq_exit();
> +}
> +
> static int gicv3_lpi_allocate_pendtable(uint64_t *reg)
> {
> uint64_t val;
> diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
> index 29c8964..8140c5f 100644
> --- a/xen/arch/arm/gic-v3.c
> +++ b/xen/arch/arm/gic-v3.c
> @@ -1674,6 +1674,7 @@ static const struct gic_hw_operations gicv3_ops = {
> .make_hwdom_dt_node = gicv3_make_hwdom_dt_node,
> .make_hwdom_madt = gicv3_make_hwdom_madt,
> .iomem_deny_access = gicv3_iomem_deny_access,
> + .do_LPI = gicv3_do_LPI,
> };
>
> static int __init gicv3_dt_preinit(struct dt_device_node *node, const void
> *data)
> diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
> index 46bb306..fd3fa05 100644
> --- a/xen/arch/arm/gic.c
> +++ b/xen/arch/arm/gic.c
> @@ -734,7 +734,13 @@ void gic_interrupt(struct cpu_user_regs *regs, int
> is_fiq)
> do_IRQ(regs, irq, is_fiq);
> local_irq_disable();
> }
> - else if (unlikely(irq < 16))
> + else if ( is_lpi(irq) )
> + {
> + local_irq_enable();
> + gic_hw_ops->do_LPI(irq);
> + local_irq_disable();
> + }
> + else if ( unlikely(irq < 16) )
> {
> do_sgi(regs, irq);
> }
> diff --git a/xen/arch/arm/vgic-v2.c b/xen/arch/arm/vgic-v2.c
> index 0587569..df91940 100644
> --- a/xen/arch/arm/vgic-v2.c
> +++ b/xen/arch/arm/vgic-v2.c
> @@ -709,11 +709,18 @@ static struct pending_irq
> *vgic_v2_lpi_to_pending(struct domain *d,
> BUG();
> }
>
> +static int vgic_v2_lpi_get_priority(struct domain *d, unsigned int vlpi)
> +{
> + /* Dummy function, no LPIs on a VGICv2. */
> + BUG();
> +}
> +
> static const struct vgic_ops vgic_v2_ops = {
> .vcpu_init = vgic_v2_vcpu_init,
> .domain_init = vgic_v2_domain_init,
> .domain_free = vgic_v2_domain_free,
> .lpi_to_pending = vgic_v2_lpi_to_pending,
> + .lpi_get_priority = vgic_v2_lpi_get_priority,
> .max_vcpus = 8,
> };
>
> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
> index 44d2b50..87f58f6 100644
> --- a/xen/arch/arm/vgic-v3.c
> +++ b/xen/arch/arm/vgic-v3.c
> @@ -1567,12 +1567,30 @@ static struct pending_irq
> *vgic_v3_lpi_to_pending(struct domain *d,
> return pirq;
> }
>
> +/* Retrieve the priority of an LPI from its struct pending_irq. */
> +static int vgic_v3_lpi_get_priority(struct domain *d, uint32_t vlpi)
> +{
> + struct pending_irq *p = vgic_v3_lpi_to_pending(d, vlpi);
> +
> + /*
> + * Cope with the case where this function is called with an invalid LPI.
> + * It is expected that a caller will bail out handling this LPI at a
> + * later point in time, but for the sake of this function let us return
> + * some value here and avoid a NULL pointer dereference.
> + */
> + if ( !p )
> + return 0xff;
> +
> + return p->lpi_priority;
> +}
> +
> static const struct vgic_ops v3_ops = {
> .vcpu_init = vgic_v3_vcpu_init,
> .domain_init = vgic_v3_domain_init,
> .domain_free = vgic_v3_domain_free,
> .emulate_reg = vgic_v3_emulate_reg,
> .lpi_to_pending = vgic_v3_lpi_to_pending,
> + .lpi_get_priority = vgic_v3_lpi_get_priority,
> /*
> * We use both AFF1 and AFF0 in (v)MPIDR. Thus, the max number of CPU
> * that can be supported is up to 4096(==256*16) in theory.
> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
> index bf6fb60..c29ad5e 100644
> --- a/xen/arch/arm/vgic.c
> +++ b/xen/arch/arm/vgic.c
> @@ -226,10 +226,15 @@ struct vcpu *vgic_get_target_vcpu(struct vcpu *v,
> unsigned int virq)
>
> static int vgic_get_virq_priority(struct vcpu *v, unsigned int virq)
> {
> - struct vgic_irq_rank *rank = vgic_rank_irq(v, virq);
> + struct vgic_irq_rank *rank;
> unsigned long flags;
> int priority;
>
> + /* LPIs don't have a rank, also store their priority separately. */
> + if ( is_lpi(virq) )
> + return v->domain->arch.vgic.handler->lpi_get_priority(v->domain,
> virq);
> +
> + rank = vgic_rank_irq(v, virq);
> vgic_lock_rank(v, rank, flags);
> priority = rank->priority[virq & INTERRUPT_RANK_MASK];
> vgic_unlock_rank(v, rank, flags);
> diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
> index 3d8e84c..ebaea35 100644
> --- a/xen/include/asm-arm/domain.h
> +++ b/xen/include/asm-arm/domain.h
> @@ -260,7 +260,8 @@ struct arch_vcpu
>
> /* GICv3: redistributor base and flags for this vCPU */
> paddr_t rdist_base;
> -#define VGIC_V3_RDIST_LAST (1 << 0) /* last vCPU of the rdist */
> +#define VGIC_V3_RDIST_LAST (1 << 0) /* last vCPU of the rdist */
> +#define VGIC_V3_LPIS_ENABLED (1 << 1)
> uint8_t flags;
> } vgic;
>
> diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h
> index 836a103..42963c0 100644
> --- a/xen/include/asm-arm/gic.h
> +++ b/xen/include/asm-arm/gic.h
> @@ -366,6 +366,8 @@ struct gic_hw_operations {
> int (*map_hwdom_extra_mappings)(struct domain *d);
> /* Deny access to GIC regions */
> int (*iomem_deny_access)(const struct domain *d);
> + /* Handle LPIs, which require special handling */
> + void (*do_LPI)(unsigned int lpi);
> };
>
> void register_gic_ops(const struct gic_hw_operations *ops);
> diff --git a/xen/include/asm-arm/gic_v3_its.h
> b/xen/include/asm-arm/gic_v3_its.h
> index 29559a3..7470779 100644
> --- a/xen/include/asm-arm/gic_v3_its.h
> +++ b/xen/include/asm-arm/gic_v3_its.h
> @@ -134,6 +134,8 @@ void gicv3_its_dt_init(const struct dt_device_node *node);
>
> bool gicv3_its_host_has_its(void);
>
> +void gicv3_do_LPI(unsigned int lpi);
> +
> int gicv3_lpi_init_rdist(void __iomem * rdist_base);
>
> /* Initialize the host structures for LPIs and the host ITSes. */
> @@ -175,6 +177,12 @@ static inline bool gicv3_its_host_has_its(void)
> return false;
> }
>
> +static inline void gicv3_do_LPI(unsigned int lpi)
> +{
> + /* We don't enable LPIs without an ITS. */
> + BUG();
> +}
> +
> static inline int gicv3_lpi_init_rdist(void __iomem * rdist_base)
> {
> return -ENODEV;
> diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
> index c9075a9..7efa164 100644
> --- a/xen/include/asm-arm/vgic.h
> +++ b/xen/include/asm-arm/vgic.h
> @@ -72,6 +72,7 @@ struct pending_irq
> #define GIC_INVALID_LR (uint8_t)~0
> uint8_t lr;
> uint8_t priority;
> + uint8_t lpi_priority; /* Caches the priority if this is an LPI. */
> /* inflight is used to append instances of pending_irq to
> * vgic.inflight_irqs */
> struct list_head inflight;
> @@ -136,6 +137,7 @@ struct vgic_ops {
> bool (*emulate_reg)(struct cpu_user_regs *regs, union hsr hsr);
> /* lookup the struct pending_irq for a given LPI interrupt */
> struct pending_irq *(*lpi_to_pending)(struct domain *d, unsigned int
> vlpi);
> + int (*lpi_get_priority)(struct domain *d, uint32_t vlpi);
> /* Maximum number of vCPU supported */
> const unsigned int max_vcpus;
> };
> --
> 2.9.0
>
>
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@xxxxxxxxxxxxx
> https://lists.xen.org/xen-devel
>
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |