|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH 07/12] ARM: VGIC: split gic.c to observe hardware/virtual GIC separation
On Thu, 19 Oct 2017, Andre Przywara wrote:
> Currently gic.c holds code to handle hardware IRQs as well as code to
> bridge VGIC requests to the GIC virtualization hardware.
That is true, however, I don't necessarely see "the code to bridge VGIC
requests to the GIC virtualization hardware" as belonging to the VGIC. I
think is a good abstraction to place in the GIC. That said, see below.
> Despite being named gic.c, this file reaches into the VGIC and uses data
> structures describing virtual IRQs.
> To improve abstraction, move the VGIC functions into a separate file,
> so that gic.c does what is says on the tin.
Splitting "the code to bridge VGIC requests to the GIC virtualization
hardware" is harmless, so I am OK with this patch.
One cosmetic comment below.
> Signed-off-by: Andre Przywara <andre.przywara@xxxxxxx>
> ---
> xen/arch/arm/Makefile | 1 +
> xen/arch/arm/gic-vgic.c | 395
> ++++++++++++++++++++++++++++++++++++++++++++++++
> xen/arch/arm/gic.c | 348 +-----------------------------------------
> 3 files changed, 398 insertions(+), 346 deletions(-)
> create mode 100644 xen/arch/arm/gic-vgic.c
>
> diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
> index 30a2a6500a..41d7366527 100644
> --- a/xen/arch/arm/Makefile
> +++ b/xen/arch/arm/Makefile
> @@ -16,6 +16,7 @@ obj-y += domain_build.o
> obj-y += domctl.o
> obj-$(EARLY_PRINTK) += early_printk.o
> obj-y += gic.o
> +obj-y += gic-vgic.o
> obj-y += gic-v2.o
> obj-$(CONFIG_HAS_GICV3) += gic-v3.o
> obj-$(CONFIG_HAS_ITS) += gic-v3-its.o
> diff --git a/xen/arch/arm/gic-vgic.c b/xen/arch/arm/gic-vgic.c
> new file mode 100644
> index 0000000000..66cae21e82
> --- /dev/null
> +++ b/xen/arch/arm/gic-vgic.c
> @@ -0,0 +1,395 @@
> +/*
> + * xen/arch/arm/gic-vgic.c
> + *
> + * ARM Generic Interrupt Controller virtualization support
> + *
> + * Tim Deegan <tim@xxxxxxx>
> + * Copyright (c) 2011 Citrix Systems.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <xen/lib.h>
> +#include <xen/init.h>
> +#include <xen/mm.h>
> +#include <xen/irq.h>
> +#include <xen/sched.h>
> +#include <xen/errno.h>
> +#include <xen/softirq.h>
> +#include <xen/list.h>
> +#include <xen/device_tree.h>
> +#include <xen/acpi.h>
> +#include <asm/p2m.h>
> +#include <asm/domain.h>
> +#include <asm/platform.h>
> +#include <asm/device.h>
> +#include <asm/io.h>
> +#include <asm/gic.h>
> +#include <asm/vgic.h>
> +#include <asm/acpi.h>
> +
> +extern uint64_t per_cpu__lr_mask;
This is a bit ugly. Would the macro "get_per_cpu_var" help?
> +extern const struct gic_hw_operations *gic_hw_ops;
> +
> +#define lr_all_full() (this_cpu(lr_mask) == ((1 << gic_hw_ops->info->nr_lrs)
> - 1))
> +
> +#undef GIC_DEBUG
> +
> +static void gic_update_one_lr(struct vcpu *v, int i);
> +
> +static inline void gic_set_lr(int lr, struct pending_irq *p,
> + unsigned int state)
> +{
> + ASSERT(!local_irq_is_enabled());
> +
> + clear_bit(GIC_IRQ_GUEST_PRISTINE_LPI, &p->status);
> +
> + gic_hw_ops->update_lr(lr, p, state);
> +
> + set_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
> + clear_bit(GIC_IRQ_GUEST_QUEUED, &p->status);
> + p->lr = lr;
> +}
> +
> +static inline void gic_add_to_lr_pending(struct vcpu *v, struct pending_irq
> *n)
> +{
> + struct pending_irq *iter;
> +
> + ASSERT(spin_is_locked(&v->arch.vgic.lock));
> +
> + if ( !list_empty(&n->lr_queue) )
> + return;
> +
> + list_for_each_entry ( iter, &v->arch.vgic.lr_pending, lr_queue )
> + {
> + if ( iter->priority > n->priority )
> + {
> + list_add_tail(&n->lr_queue, &iter->lr_queue);
> + return;
> + }
> + }
> + list_add_tail(&n->lr_queue, &v->arch.vgic.lr_pending);
> +}
> +
> +void gic_raise_inflight_irq(struct vcpu *v, unsigned int virtual_irq)
> +{
> + struct pending_irq *n = irq_to_pending(v, virtual_irq);
> +
> + /* If an LPI has been removed meanwhile, there is nothing left to raise.
> */
> + if ( unlikely(!n) )
> + return;
> +
> + ASSERT(spin_is_locked(&v->arch.vgic.lock));
> +
> + /* Don't try to update the LR if the interrupt is disabled */
> + if ( !test_bit(GIC_IRQ_GUEST_ENABLED, &n->status) )
> + return;
> +
> + if ( list_empty(&n->lr_queue) )
> + {
> + if ( v == current )
> + gic_update_one_lr(v, n->lr);
> + }
> +#ifdef GIC_DEBUG
> + else
> + gdprintk(XENLOG_DEBUG, "trying to inject irq=%u into d%dv%d, when it
> is still lr_pending\n",
> + virtual_irq, v->domain->domain_id, v->vcpu_id);
> +#endif
> +}
> +
> +/*
> + * Find an unused LR to insert an IRQ into, starting with the LR given
> + * by @lr. If this new interrupt is a PRISTINE LPI, scan the other LRs to
> + * avoid inserting the same IRQ twice. This situation can occur when an
> + * event gets discarded while the LPI is in an LR, and a new LPI with the
> + * same number gets mapped quickly afterwards.
> + */
> +static unsigned int gic_find_unused_lr(struct vcpu *v,
> + struct pending_irq *p,
> + unsigned int lr)
> +{
> + unsigned int nr_lrs = gic_hw_ops->info->nr_lrs;
> + unsigned long *lr_mask = (unsigned long *) &this_cpu(lr_mask);
> + struct gic_lr lr_val;
> +
> + ASSERT(spin_is_locked(&v->arch.vgic.lock));
> +
> + if ( unlikely(test_bit(GIC_IRQ_GUEST_PRISTINE_LPI, &p->status)) )
> + {
> + unsigned int used_lr;
> +
> + for_each_set_bit(used_lr, lr_mask, nr_lrs)
> + {
> + gic_hw_ops->read_lr(used_lr, &lr_val);
> + if ( lr_val.virq == p->irq )
> + return used_lr;
> + }
> + }
> +
> + lr = find_next_zero_bit(lr_mask, nr_lrs, lr);
> +
> + return lr;
> +}
> +
> +void gic_raise_guest_irq(struct vcpu *v, unsigned int virtual_irq,
> + unsigned int priority)
> +{
> + int i;
> + unsigned int nr_lrs = gic_hw_ops->info->nr_lrs;
> + struct pending_irq *p = irq_to_pending(v, virtual_irq);
> +
> + ASSERT(spin_is_locked(&v->arch.vgic.lock));
> +
> + if ( unlikely(!p) )
> + /* An unmapped LPI does not need to be raised. */
> + return;
> +
> + if ( v == current && list_empty(&v->arch.vgic.lr_pending) )
> + {
> + i = gic_find_unused_lr(v, p, 0);
> +
> + if (i < nr_lrs) {
> + set_bit(i, &this_cpu(lr_mask));
> + gic_set_lr(i, p, GICH_LR_PENDING);
> + return;
> + }
> + }
> +
> + gic_add_to_lr_pending(v, p);
> +}
> +
> +static void gic_update_one_lr(struct vcpu *v, int i)
> +{
> + struct pending_irq *p;
> + int irq;
> + struct gic_lr lr_val;
> +
> + ASSERT(spin_is_locked(&v->arch.vgic.lock));
> + ASSERT(!local_irq_is_enabled());
> +
> + gic_hw_ops->read_lr(i, &lr_val);
> + irq = lr_val.virq;
> + p = irq_to_pending(v, irq);
> + /*
> + * An LPI might have been unmapped, in which case we just clean up here.
> + * If that LPI is marked as PRISTINE, the information in the LR is bogus,
> + * as it belongs to a previous, already unmapped LPI. So we discard it
> + * here as well.
> + */
> + if ( unlikely(!p ||
> + test_and_clear_bit(GIC_IRQ_GUEST_PRISTINE_LPI,
> &p->status)) )
> + {
> + ASSERT(is_lpi(irq));
> +
> + gic_hw_ops->clear_lr(i);
> + clear_bit(i, &this_cpu(lr_mask));
> +
> + return;
> + }
> +
> + if ( lr_val.state & GICH_LR_ACTIVE )
> + {
> + set_bit(GIC_IRQ_GUEST_ACTIVE, &p->status);
> + if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) &&
> + test_and_clear_bit(GIC_IRQ_GUEST_QUEUED, &p->status) )
> + {
> + if ( p->desc == NULL )
> + {
> + lr_val.state |= GICH_LR_PENDING;
> + gic_hw_ops->write_lr(i, &lr_val);
> + }
> + else
> + gdprintk(XENLOG_WARNING, "unable to inject hw irq=%d into
> d%dv%d: already active in LR%d\n",
> + irq, v->domain->domain_id, v->vcpu_id, i);
> + }
> + }
> + else if ( lr_val.state & GICH_LR_PENDING )
> + {
> + int q __attribute__ ((unused)) =
> test_and_clear_bit(GIC_IRQ_GUEST_QUEUED, &p->status);
> +#ifdef GIC_DEBUG
> + if ( q )
> + gdprintk(XENLOG_DEBUG, "trying to inject irq=%d into d%dv%d,
> when it is already pending in LR%d\n",
> + irq, v->domain->domain_id, v->vcpu_id, i);
> +#endif
> + }
> + else
> + {
> + gic_hw_ops->clear_lr(i);
> + clear_bit(i, &this_cpu(lr_mask));
> +
> + if ( p->desc != NULL )
> + clear_bit(_IRQ_INPROGRESS, &p->desc->status);
> + clear_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
> + clear_bit(GIC_IRQ_GUEST_ACTIVE, &p->status);
> + p->lr = GIC_INVALID_LR;
> + if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) &&
> + test_bit(GIC_IRQ_GUEST_QUEUED, &p->status) &&
> + !test_bit(GIC_IRQ_GUEST_MIGRATING, &p->status) )
> + gic_raise_guest_irq(v, irq, p->priority);
> + else {
> + list_del_init(&p->inflight);
> + /*
> + * Remove from inflight, then change physical affinity. It
> + * makes sure that when a new interrupt is received on the
> + * next pcpu, inflight is already cleared. No concurrent
> + * accesses to inflight.
> + */
> + smp_wmb();
> + if ( test_bit(GIC_IRQ_GUEST_MIGRATING, &p->status) )
> + {
> + struct vcpu *v_target = vgic_get_target_vcpu(v, irq);
> + irq_set_affinity(p->desc, cpumask_of(v_target->processor));
> + clear_bit(GIC_IRQ_GUEST_MIGRATING, &p->status);
> + }
> + }
> + }
> +}
> +
> +void gic_clear_lrs(struct vcpu *v)
> +{
> + int i = 0;
> + unsigned long flags;
> + unsigned int nr_lrs = gic_hw_ops->info->nr_lrs;
> +
> + /* The idle domain has no LRs to be cleared. Since gic_restore_state
> + * doesn't write any LR registers for the idle domain they could be
> + * non-zero. */
> + if ( is_idle_vcpu(v) )
> + return;
> +
> + gic_hw_ops->update_hcr_status(GICH_HCR_UIE, 0);
> +
> + spin_lock_irqsave(&v->arch.vgic.lock, flags);
> +
> + while ((i = find_next_bit((const unsigned long *) &this_cpu(lr_mask),
> + nr_lrs, i)) < nr_lrs ) {
> + gic_update_one_lr(v, i);
> + i++;
> + }
> +
> + spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
> +}
> +
> +static void gic_restore_pending_irqs(struct vcpu *v)
> +{
> + int lr = 0;
> + struct pending_irq *p, *t, *p_r;
> + struct list_head *inflight_r;
> + unsigned long flags;
> + unsigned int nr_lrs = gic_hw_ops->info->nr_lrs;
> + int lrs = nr_lrs;
> +
> + spin_lock_irqsave(&v->arch.vgic.lock, flags);
> +
> + if ( list_empty(&v->arch.vgic.lr_pending) )
> + goto out;
> +
> + inflight_r = &v->arch.vgic.inflight_irqs;
> + list_for_each_entry_safe ( p, t, &v->arch.vgic.lr_pending, lr_queue )
> + {
> + lr = gic_find_unused_lr(v, p, lr);
> + if ( lr >= nr_lrs )
> + {
> + /* No more free LRs: find a lower priority irq to evict */
> + list_for_each_entry_reverse( p_r, inflight_r, inflight )
> + {
> + if ( p_r->priority == p->priority )
> + goto out;
> + if ( test_bit(GIC_IRQ_GUEST_VISIBLE, &p_r->status) &&
> + !test_bit(GIC_IRQ_GUEST_ACTIVE, &p_r->status) )
> + goto found;
> + }
> + /* We didn't find a victim this time, and we won't next
> + * time, so quit */
> + goto out;
> +
> +found:
> + lr = p_r->lr;
> + p_r->lr = GIC_INVALID_LR;
> + set_bit(GIC_IRQ_GUEST_QUEUED, &p_r->status);
> + clear_bit(GIC_IRQ_GUEST_VISIBLE, &p_r->status);
> + gic_add_to_lr_pending(v, p_r);
> + inflight_r = &p_r->inflight;
> + }
> +
> + gic_set_lr(lr, p, GICH_LR_PENDING);
> + list_del_init(&p->lr_queue);
> + set_bit(lr, &this_cpu(lr_mask));
> +
> + /* We can only evict nr_lrs entries */
> + lrs--;
> + if ( lrs == 0 )
> + break;
> + }
> +
> +out:
> + spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
> +}
> +
> +int gic_events_need_delivery(void)
> +{
> + struct vcpu *v = current;
> + struct pending_irq *p;
> + unsigned long flags;
> + const unsigned long apr = gic_hw_ops->read_apr(0);
> + int mask_priority;
> + int active_priority;
> + int rc = 0;
> +
> + mask_priority = gic_hw_ops->read_vmcr_priority();
> + active_priority = find_next_bit(&apr, 32, 0);
> +
> + spin_lock_irqsave(&v->arch.vgic.lock, flags);
> +
> + /* TODO: We order the guest irqs by priority, but we don't change
> + * the priority of host irqs. */
> +
> + /* find the first enabled non-active irq, the queue is already
> + * ordered by priority */
> + list_for_each_entry( p, &v->arch.vgic.inflight_irqs, inflight )
> + {
> + if ( GIC_PRI_TO_GUEST(p->priority) >= mask_priority )
> + goto out;
> + if ( GIC_PRI_TO_GUEST(p->priority) >= active_priority )
> + goto out;
> + if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) )
> + {
> + rc = 1;
> + goto out;
> + }
> + }
> +
> +out:
> + spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
> + return rc;
> +}
> +
> +void gic_inject(struct vcpu *v)
> +{
> + ASSERT(!local_irq_is_enabled());
> +
> + gic_restore_pending_irqs(v);
> +
> + if ( v != current )
> + return;
> +
> + if ( !list_empty(¤t->arch.vgic.lr_pending) && lr_all_full() )
> + gic_hw_ops->update_hcr_status(GICH_HCR_UIE, 1);
> +}
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
> index 58d69955fb..04e6d66b69 100644
> --- a/xen/arch/arm/gic.c
> +++ b/xen/arch/arm/gic.c
> @@ -36,15 +36,11 @@
> #include <asm/vgic.h>
> #include <asm/acpi.h>
>
> -static DEFINE_PER_CPU(uint64_t, lr_mask);
> -
> -#define lr_all_full() (this_cpu(lr_mask) == ((1 << gic_hw_ops->info->nr_lrs)
> - 1))
> +DEFINE_PER_CPU(uint64_t, lr_mask);
I didn't look at what's left in gic.c, but would it be possible to move
the definition of lr_mask to gic-vgic.c?
> #undef GIC_DEBUG
>
> -static void gic_update_one_lr(struct vcpu *v, int i);
> -
> -static const struct gic_hw_operations *gic_hw_ops;
> +const struct gic_hw_operations *gic_hw_ops;
>
> void register_gic_ops(const struct gic_hw_operations *ops)
> {
> @@ -366,346 +362,6 @@ void gic_disable_cpu(void)
> gic_hw_ops->disable_interface();
> }
>
> -static inline void gic_set_lr(int lr, struct pending_irq *p,
> - unsigned int state)
> -{
> - ASSERT(!local_irq_is_enabled());
> -
> - clear_bit(GIC_IRQ_GUEST_PRISTINE_LPI, &p->status);
> -
> - gic_hw_ops->update_lr(lr, p, state);
> -
> - set_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
> - clear_bit(GIC_IRQ_GUEST_QUEUED, &p->status);
> - p->lr = lr;
> -}
> -
> -static inline void gic_add_to_lr_pending(struct vcpu *v, struct pending_irq
> *n)
> -{
> - struct pending_irq *iter;
> -
> - ASSERT(spin_is_locked(&v->arch.vgic.lock));
> -
> - if ( !list_empty(&n->lr_queue) )
> - return;
> -
> - list_for_each_entry ( iter, &v->arch.vgic.lr_pending, lr_queue )
> - {
> - if ( iter->priority > n->priority )
> - {
> - list_add_tail(&n->lr_queue, &iter->lr_queue);
> - return;
> - }
> - }
> - list_add_tail(&n->lr_queue, &v->arch.vgic.lr_pending);
> -}
> -
> -void gic_raise_inflight_irq(struct vcpu *v, unsigned int virtual_irq)
> -{
> - struct pending_irq *n = irq_to_pending(v, virtual_irq);
> -
> - /* If an LPI has been removed meanwhile, there is nothing left to raise.
> */
> - if ( unlikely(!n) )
> - return;
> -
> - ASSERT(spin_is_locked(&v->arch.vgic.lock));
> -
> - /* Don't try to update the LR if the interrupt is disabled */
> - if ( !test_bit(GIC_IRQ_GUEST_ENABLED, &n->status) )
> - return;
> -
> - if ( list_empty(&n->lr_queue) )
> - {
> - if ( v == current )
> - gic_update_one_lr(v, n->lr);
> - }
> -#ifdef GIC_DEBUG
> - else
> - gdprintk(XENLOG_DEBUG, "trying to inject irq=%u into d%dv%d, when it
> is still lr_pending\n",
> - virtual_irq, v->domain->domain_id, v->vcpu_id);
> -#endif
> -}
> -
> -/*
> - * Find an unused LR to insert an IRQ into, starting with the LR given
> - * by @lr. If this new interrupt is a PRISTINE LPI, scan the other LRs to
> - * avoid inserting the same IRQ twice. This situation can occur when an
> - * event gets discarded while the LPI is in an LR, and a new LPI with the
> - * same number gets mapped quickly afterwards.
> - */
> -static unsigned int gic_find_unused_lr(struct vcpu *v,
> - struct pending_irq *p,
> - unsigned int lr)
> -{
> - unsigned int nr_lrs = gic_hw_ops->info->nr_lrs;
> - unsigned long *lr_mask = (unsigned long *) &this_cpu(lr_mask);
> - struct gic_lr lr_val;
> -
> - ASSERT(spin_is_locked(&v->arch.vgic.lock));
> -
> - if ( unlikely(test_bit(GIC_IRQ_GUEST_PRISTINE_LPI, &p->status)) )
> - {
> - unsigned int used_lr;
> -
> - for_each_set_bit(used_lr, lr_mask, nr_lrs)
> - {
> - gic_hw_ops->read_lr(used_lr, &lr_val);
> - if ( lr_val.virq == p->irq )
> - return used_lr;
> - }
> - }
> -
> - lr = find_next_zero_bit(lr_mask, nr_lrs, lr);
> -
> - return lr;
> -}
> -
> -void gic_raise_guest_irq(struct vcpu *v, unsigned int virtual_irq,
> - unsigned int priority)
> -{
> - int i;
> - unsigned int nr_lrs = gic_hw_ops->info->nr_lrs;
> - struct pending_irq *p = irq_to_pending(v, virtual_irq);
> -
> - ASSERT(spin_is_locked(&v->arch.vgic.lock));
> -
> - if ( unlikely(!p) )
> - /* An unmapped LPI does not need to be raised. */
> - return;
> -
> - if ( v == current && list_empty(&v->arch.vgic.lr_pending) )
> - {
> - i = gic_find_unused_lr(v, p, 0);
> -
> - if (i < nr_lrs) {
> - set_bit(i, &this_cpu(lr_mask));
> - gic_set_lr(i, p, GICH_LR_PENDING);
> - return;
> - }
> - }
> -
> - gic_add_to_lr_pending(v, p);
> -}
> -
> -static void gic_update_one_lr(struct vcpu *v, int i)
> -{
> - struct pending_irq *p;
> - int irq;
> - struct gic_lr lr_val;
> -
> - ASSERT(spin_is_locked(&v->arch.vgic.lock));
> - ASSERT(!local_irq_is_enabled());
> -
> - gic_hw_ops->read_lr(i, &lr_val);
> - irq = lr_val.virq;
> - p = irq_to_pending(v, irq);
> - /*
> - * An LPI might have been unmapped, in which case we just clean up here.
> - * If that LPI is marked as PRISTINE, the information in the LR is bogus,
> - * as it belongs to a previous, already unmapped LPI. So we discard it
> - * here as well.
> - */
> - if ( unlikely(!p ||
> - test_and_clear_bit(GIC_IRQ_GUEST_PRISTINE_LPI,
> &p->status)) )
> - {
> - ASSERT(is_lpi(irq));
> -
> - gic_hw_ops->clear_lr(i);
> - clear_bit(i, &this_cpu(lr_mask));
> -
> - return;
> - }
> -
> - if ( lr_val.state & GICH_LR_ACTIVE )
> - {
> - set_bit(GIC_IRQ_GUEST_ACTIVE, &p->status);
> - if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) &&
> - test_and_clear_bit(GIC_IRQ_GUEST_QUEUED, &p->status) )
> - {
> - if ( p->desc == NULL )
> - {
> - lr_val.state |= GICH_LR_PENDING;
> - gic_hw_ops->write_lr(i, &lr_val);
> - }
> - else
> - gdprintk(XENLOG_WARNING, "unable to inject hw irq=%d into
> d%dv%d: already active in LR%d\n",
> - irq, v->domain->domain_id, v->vcpu_id, i);
> - }
> - }
> - else if ( lr_val.state & GICH_LR_PENDING )
> - {
> - int q __attribute__ ((unused)) =
> test_and_clear_bit(GIC_IRQ_GUEST_QUEUED, &p->status);
> -#ifdef GIC_DEBUG
> - if ( q )
> - gdprintk(XENLOG_DEBUG, "trying to inject irq=%d into d%dv%d,
> when it is already pending in LR%d\n",
> - irq, v->domain->domain_id, v->vcpu_id, i);
> -#endif
> - }
> - else
> - {
> - gic_hw_ops->clear_lr(i);
> - clear_bit(i, &this_cpu(lr_mask));
> -
> - if ( p->desc != NULL )
> - clear_bit(_IRQ_INPROGRESS, &p->desc->status);
> - clear_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
> - clear_bit(GIC_IRQ_GUEST_ACTIVE, &p->status);
> - p->lr = GIC_INVALID_LR;
> - if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) &&
> - test_bit(GIC_IRQ_GUEST_QUEUED, &p->status) &&
> - !test_bit(GIC_IRQ_GUEST_MIGRATING, &p->status) )
> - gic_raise_guest_irq(v, irq, p->priority);
> - else {
> - list_del_init(&p->inflight);
> - /*
> - * Remove from inflight, then change physical affinity. It
> - * makes sure that when a new interrupt is received on the
> - * next pcpu, inflight is already cleared. No concurrent
> - * accesses to inflight.
> - */
> - smp_wmb();
> - if ( test_bit(GIC_IRQ_GUEST_MIGRATING, &p->status) )
> - {
> - struct vcpu *v_target = vgic_get_target_vcpu(v, irq);
> - irq_set_affinity(p->desc, cpumask_of(v_target->processor));
> - clear_bit(GIC_IRQ_GUEST_MIGRATING, &p->status);
> - }
> - }
> - }
> -}
> -
> -void gic_clear_lrs(struct vcpu *v)
> -{
> - int i = 0;
> - unsigned long flags;
> - unsigned int nr_lrs = gic_hw_ops->info->nr_lrs;
> -
> - /* The idle domain has no LRs to be cleared. Since gic_restore_state
> - * doesn't write any LR registers for the idle domain they could be
> - * non-zero. */
> - if ( is_idle_vcpu(v) )
> - return;
> -
> - gic_hw_ops->update_hcr_status(GICH_HCR_UIE, false);
> -
> - spin_lock_irqsave(&v->arch.vgic.lock, flags);
> -
> - while ((i = find_next_bit((const unsigned long *) &this_cpu(lr_mask),
> - nr_lrs, i)) < nr_lrs ) {
> - gic_update_one_lr(v, i);
> - i++;
> - }
> -
> - spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
> -}
> -
> -static void gic_restore_pending_irqs(struct vcpu *v)
> -{
> - int lr = 0;
> - struct pending_irq *p, *t, *p_r;
> - struct list_head *inflight_r;
> - unsigned long flags;
> - unsigned int nr_lrs = gic_hw_ops->info->nr_lrs;
> - int lrs = nr_lrs;
> -
> - spin_lock_irqsave(&v->arch.vgic.lock, flags);
> -
> - if ( list_empty(&v->arch.vgic.lr_pending) )
> - goto out;
> -
> - inflight_r = &v->arch.vgic.inflight_irqs;
> - list_for_each_entry_safe ( p, t, &v->arch.vgic.lr_pending, lr_queue )
> - {
> - lr = gic_find_unused_lr(v, p, lr);
> - if ( lr >= nr_lrs )
> - {
> - /* No more free LRs: find a lower priority irq to evict */
> - list_for_each_entry_reverse( p_r, inflight_r, inflight )
> - {
> - if ( p_r->priority == p->priority )
> - goto out;
> - if ( test_bit(GIC_IRQ_GUEST_VISIBLE, &p_r->status) &&
> - !test_bit(GIC_IRQ_GUEST_ACTIVE, &p_r->status) )
> - goto found;
> - }
> - /* We didn't find a victim this time, and we won't next
> - * time, so quit */
> - goto out;
> -
> -found:
> - lr = p_r->lr;
> - p_r->lr = GIC_INVALID_LR;
> - set_bit(GIC_IRQ_GUEST_QUEUED, &p_r->status);
> - clear_bit(GIC_IRQ_GUEST_VISIBLE, &p_r->status);
> - gic_add_to_lr_pending(v, p_r);
> - inflight_r = &p_r->inflight;
> - }
> -
> - gic_set_lr(lr, p, GICH_LR_PENDING);
> - list_del_init(&p->lr_queue);
> - set_bit(lr, &this_cpu(lr_mask));
> -
> - /* We can only evict nr_lrs entries */
> - lrs--;
> - if ( lrs == 0 )
> - break;
> - }
> -
> -out:
> - spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
> -}
> -
> -int gic_events_need_delivery(void)
> -{
> - struct vcpu *v = current;
> - struct pending_irq *p;
> - unsigned long flags;
> - const unsigned long apr = gic_hw_ops->read_apr(0);
> - int mask_priority;
> - int active_priority;
> - int rc = 0;
> -
> - mask_priority = gic_hw_ops->read_vmcr_priority();
> - active_priority = find_next_bit(&apr, 32, 0);
> -
> - spin_lock_irqsave(&v->arch.vgic.lock, flags);
> -
> - /* TODO: We order the guest irqs by priority, but we don't change
> - * the priority of host irqs. */
> -
> - /* find the first enabled non-active irq, the queue is already
> - * ordered by priority */
> - list_for_each_entry( p, &v->arch.vgic.inflight_irqs, inflight )
> - {
> - if ( GIC_PRI_TO_GUEST(p->priority) >= mask_priority )
> - goto out;
> - if ( GIC_PRI_TO_GUEST(p->priority) >= active_priority )
> - goto out;
> - if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) )
> - {
> - rc = 1;
> - goto out;
> - }
> - }
> -
> -out:
> - spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
> - return rc;
> -}
> -
> -void gic_inject(struct vcpu *v)
> -{
> - ASSERT(!local_irq_is_enabled());
> -
> - gic_restore_pending_irqs(v);
> -
> - if ( v != current )
> - return;
> -
> - if ( !list_empty(¤t->arch.vgic.lr_pending) && lr_all_full() )
> - gic_hw_ops->update_hcr_status(GICH_HCR_UIE, true);
> -}
> -
> static void do_sgi(struct cpu_user_regs *regs, enum gic_sgi sgi)
> {
> /* Lower the priority */
> --
> 2.14.1
>
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |