|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v9 20/28] ARM: GICv3: handle unmapped LPIs
Hi Stefano,
On 20/05/17 02:25, Stefano Stabellini wrote:
> On Thu, 11 May 2017, Andre Przywara wrote:
>> When LPIs get unmapped by a guest, they might still be in some LR of
>> some VCPU. Nevertheless we remove the corresponding pending_irq
>> (possibly freeing it), and detect this case (irq_to_pending() returns
>> NULL) when the LR gets cleaned up later.
>> However a *new* LPI may get mapped with the same number while the old
>> LPI is *still* in some LR. To avoid getting the wrong state, we mark
>> every newly mapped LPI as PRISTINE, which means: has never been in an
>> LR before. If we detect the LPI in an LR anyway, it must have been an
>> older one, which we can simply retire.
>> Before inserting such a PRISTINE LPI into an LR, we must make sure that
>> it's not already in another LR, as the architecture forbids two
>> interrupts with the same virtual IRQ number on one CPU.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@xxxxxxx>
>> ---
>> xen/arch/arm/gic.c | 55
>> +++++++++++++++++++++++++++++++++++++++++-----
>> xen/include/asm-arm/vgic.h | 6 +++++
>> 2 files changed, 56 insertions(+), 5 deletions(-)
>>
>> diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
>> index fd3fa05..8bf0578 100644
>> --- a/xen/arch/arm/gic.c
>> +++ b/xen/arch/arm/gic.c
>> @@ -375,6 +375,8 @@ static inline void gic_set_lr(int lr, struct pending_irq
>> *p,
>> {
>> 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);
>> @@ -442,12 +444,41 @@ void gic_raise_inflight_irq(struct vcpu *v, unsigned
>> int virtual_irq)
>> #endif
>> }
>>
>> +/*
>> + * Find an unused LR to insert an IRQ into. If this new interrupt is a
>> + * PRISTINE LPI, scan the other LRs to avoid inserting the same IRQ twice.
>> + */
>> +static int gic_find_unused_lr(struct vcpu *v, struct pending_irq *p, 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 ( test_bit(GIC_IRQ_GUEST_PRISTINE_LPI, &p->status) )
>
> Maybe we should add an "unlikely".
>
> I can see how this would be OKish at runtime, but at boot time there
> might be a bunch of PRISTINE_LPIs,
What is your concern here, performance?
Let's put this into perspective:
- The PRISTINE bit gets set upon MAPTI, which Linux usually does *once*
when the driver gets loaded. It gets cleared after the first injection.
- If that happens, we scan all LRs. Most implementations have 4(!) of
them (ARM GIC implementations, for instance), also the algorithm only
scans *used* LRs, so normally just one or two.
- Reading the LR is a *local* system register *read*, not an MMIO
access, and not propagated to other cores. Yes, this may be "costly"
(compared to other instructions), but it's probably still cheaper than a
page table walk (TLB miss) or L2 cache miss.
So to summarize: this is rare, iterates over only a very small number of
registers and is not hugely expensive.
At this point in time I would refrain from any kind of performance
optimization, at least unless we have solved all the other issues and
have done some benchmarking/profiling (on different hardware platforms).
> but no MAPDs have been issued yet, right?
As Julien already mentioned, this gets set after a MAPTI, which requires
a MAPD before.
Cheers,
Andre.
> I have a suggestion, I'll leave it to you and Julien if you want to do
> this now, or maybe consider it as a TODO item. I am OK either way (I
> don't want to delay the ITS any longer).
>
> I am thinking we should do this scanning only after at least one MAPD
> has been issued for a given cpu at least once. I would resurrect the
> idea of a DISCARD flag, but not on the pending_irq, that I believe it's
> difficult to handle, but a single global DISCARD flag per struct vcpu.
>
> On MAPD, we set DISCARD for the target vcpu of the LPI we are dropping.
> Next time we want to inject a PRISTINE_IRQ on that cpu interface, we
> scan all LRs for interrupts with a NULL pending_irq. We remove those
> from LRs, then we remove the DISCARD flag.
>
> Do you think it would work?
>
>
>> + {
>> + int used_lr = 0;
>> +
>> + while ( (used_lr = find_next_bit(lr_mask, nr_lrs, used_lr)) <
>> 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);
>> + unsigned int nr_lrs = gic_hw_ops->info->nr_lrs;
>> + int i = nr_lrs;
>>
>> ASSERT(spin_is_locked(&v->arch.vgic.lock));
>>
>> @@ -457,7 +488,8 @@ void gic_raise_guest_irq(struct vcpu *v, unsigned int
>> virtual_irq,
>>
>> if ( v == current && list_empty(&v->arch.vgic.lr_pending) )
>> {
>> - i = find_first_zero_bit(&this_cpu(lr_mask), nr_lrs);
>> + 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);
>> @@ -509,7 +541,17 @@ static void gic_update_one_lr(struct vcpu *v, int i)
>> }
>> else if ( lr_val.state & GICH_LR_PENDING )
>> {
>> - int q __attribute__ ((unused)) =
>> test_and_clear_bit(GIC_IRQ_GUEST_QUEUED, &p->status);
>> + int q __attribute__ ((unused));
>> +
>> + if ( test_and_clear_bit(GIC_IRQ_GUEST_PRISTINE_LPI, &p->status) )
>> + {
>> + gic_hw_ops->clear_lr(i);
>> + clear_bit(i, &this_cpu(lr_mask));
>> +
>> + return;
>> + }
>> +
>> + q = 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",
>> @@ -521,6 +563,9 @@ static void gic_update_one_lr(struct vcpu *v, int i)
>> gic_hw_ops->clear_lr(i);
>> clear_bit(i, &this_cpu(lr_mask));
>>
>> + if ( test_and_clear_bit(GIC_IRQ_GUEST_PRISTINE_LPI, &p->status) )
>> + return;
>> if ( p->desc != NULL )
>> clear_bit(_IRQ_INPROGRESS, &p->desc->status);
>> clear_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
>> @@ -591,7 +636,7 @@ static void gic_restore_pending_irqs(struct vcpu *v)
>> inflight_r = &v->arch.vgic.inflight_irqs;
>> list_for_each_entry_safe ( p, t, &v->arch.vgic.lr_pending, lr_queue )
>> {
>> - lr = find_next_zero_bit(&this_cpu(lr_mask), nr_lrs, lr);
>> + lr = gic_find_unused_lr(v, p, lr);
>> if ( lr >= nr_lrs )
>> {
>> /* No more free LRs: find a lower priority irq to evict */
>> diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
>> index 02732db..3fc4ceb 100644
>> --- a/xen/include/asm-arm/vgic.h
>> +++ b/xen/include/asm-arm/vgic.h
>> @@ -60,12 +60,18 @@ struct pending_irq
>> * vcpu while it is still inflight and on an GICH_LR register on the
>> * old vcpu.
>> *
>> + * GIC_IRQ_GUEST_PRISTINE_LPI: the IRQ is a newly mapped LPI, which
>> + * has never been in an LR before. This means that any trace of an
>> + * LPI with the same number in an LR must be from an older LPI, which
>> + * has been unmapped before.
>> + *
>> */
>> #define GIC_IRQ_GUEST_QUEUED 0
>> #define GIC_IRQ_GUEST_ACTIVE 1
>> #define GIC_IRQ_GUEST_VISIBLE 2
>> #define GIC_IRQ_GUEST_ENABLED 3
>> #define GIC_IRQ_GUEST_MIGRATING 4
>> +#define GIC_IRQ_GUEST_PRISTINE_LPI 5
>> unsigned long status;
>> struct irq_desc *desc; /* only set it the irq corresponds to a physical
>> irq */
>> unsigned int irq;
>> --
>> 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 |