|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [RFC PATCH] xen/arm: Deliver interrupts to vcpu specified in IROUTER
Hi Stefano,
On Sat, Sep 6, 2014 at 12:14 AM, Stefano Stabellini
<stefano.stabellini@xxxxxxxxxxxxx> wrote:
> On Fri, 5 Sep 2014, Vijay Kilari wrote:
>> On Fri, Sep 5, 2014 at 4:59 AM, Stefano Stabellini
>> <stefano.stabellini@xxxxxxxxxxxxx> wrote:
>> > On Thu, 4 Sep 2014, vijay.kilari@xxxxxxxxx wrote:
>> >> From: Vijaya Kumar K <Vijaya.Kumar@xxxxxxxxxxxxxxxxxx>
>> >>
>> >> In GICv3 use IROUTER register contents to
>> >> deliver irq to specified vcpu's.
>> >>
>> >> In error scenario fallback to vcpu 0.
>> >>
>> >> This patch is similar to Stefano's commit
>> >> 5b3a817ea33b891caf7d7d788da9ce6deffa82a1 for GICv2
>> >>
>> >> Signed-off-by: Vijaya Kumar K <Vijaya.Kumar@xxxxxxxxxxxxxxxxxx>
>> >> ---
>> >> xen/arch/arm/vgic-v3.c | 115
>> >> ++++++++++++++++++++++++++++++++++++++++++++----
>> >> 1 file changed, 106 insertions(+), 9 deletions(-)
>> >>
>> >> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
>> >> index ecda672..b01a201 100644
>> >> --- a/xen/arch/arm/vgic-v3.c
>> >> +++ b/xen/arch/arm/vgic-v3.c
>> >> @@ -45,6 +45,35 @@
>> >> #define GICV3_GICR_PIDR2 GICV3_GICD_PIDR2
>> >> #define GICV3_GICR_PIDR4 GICV3_GICD_PIDR4
>> >>
>> >> +static struct vcpu *vgicv3_irouter_to_vcpu(struct vcpu *v,
>> >> + uint64_t irouter_aff)
>> >> +{
>> >> + int i;
>> >> + uint64_t cpu_affinity;
>> >> + struct vcpu *v_target = NULL;
>> >> +
>> >> + /* If routing mode is set. Fallback to vcpu0 */
>> >> + if ( irouter_aff & GICD_IROUTER_SPI_MODE_ANY )
>> >> + return v->domain->vcpu[0];
>> >> +
>> >> + for ( i = 0; i < v->domain->max_vcpus; i++ )
>> >> + {
>> >> + v_target = v->domain->vcpu[i];
>> >> + cpu_affinity = (MPIDR_AFFINITY_LEVEL(v_target->arch.vmpidr, 3)
>> >> << 32 |
>> >> + MPIDR_AFFINITY_LEVEL(v_target->arch.vmpidr, 2)
>> >> << 16 |
>> >> + MPIDR_AFFINITY_LEVEL(v_target->arch.vmpidr, 1)
>> >> << 8 |
>> >> + MPIDR_AFFINITY_LEVEL(v_target->arch.vmpidr, 0));
>> >> +
>> >> + if ( irouter_aff == cpu_affinity )
>> >> + return v_target;
>> >> + }
>> >
>> > Using a loop is pretty bad. Couldn't you just calculate the lowest vcpu
>> > number from irouter_aff (maybe using find_first_bit)?
>>
>> IMO, If GICD_IROUTER_SPI_MODE_ANY bit is set, then it specifies any cpu.
>> If GICD_IROUTER_SPI_MODE_ANY is not set, it register specifies only
>> one cpu number.
>>
>> So we cannot use find_first_bit() unlike GICv2 where ITARGET specifies
>> irq affinity
>> to more than one CPU.
>>
>> If at all we want to avoid this for loop, then we have to maintain one
>> more variable for
>> every IROUTERn and corresponding vpcu number, which is updated on
>> IROUTERn update
>
> Given that at the moment AFF0 is just set to vcpu_id (see
> vcpu_initialise), I would simply do a direct calculation, adding a TODO
> comment so that we remember to fix this when we implement smarter mpidr
> schemes.
Instead of storing vcpu_id in AFF0 of vgic irouter[], we could rather
store vcpu_id in irouter[]
similar to v2. One bit per vcpu, though in v3 always only one bit is set.
The conversion function irouter_to_vcpu & vcpu_to_irouter will manage
mmio_{read,write}
of IROUTER regs.
Is this OK?
>
> Even better you could introduce two simple functions that convert vmpidr
> to vcpu_id and vice versa, so that we keep all the hacks in one place.
>
>
>> >> switch ( gicd_reg )
>> >> @@ -804,16 +836,42 @@ static int vgic_v3_distr_mmio_write(struct vcpu *v,
>> >> mmio_info_t *info)
>> >> if ( dabt.size != DABT_DOUBLE_WORD ) goto bad_width;
>> >> rank = vgic_rank_offset(v, 64, gicd_reg - GICD_IROUTER,
>> >> DABT_DOUBLE_WORD);
>> >> if ( rank == NULL) goto write_ignore_64;
>> >> - if ( *r )
>> >> + new_target = *r;
>> >> + vgic_lock_rank(v, rank, flags);
>> >> + old_target = rank->v3.irouter[REG_RANK_INDEX(64,
>> >> + (gicd_reg - GICD_IROUTER),
>> >> DABT_DOUBLE_WORD)];
>> >> + if ( *r & GICD_IROUTER_SPI_MODE_ANY )
>> >> {
>> >> - /* TODO: Ignored. We don't support irq delivery for vcpu !=
>> >> 0 */
>> >> - gdprintk(XENLOG_DEBUG,
>> >> - "SPI delivery to secondary cpus not supported\n");
>> >> - goto write_ignore_64;
>> >> + /*
>> >> + * IRQ routing mode set. Route any one processor in the
>> >> entire
>> >> + * system. We chose vcpu 0
>> >> + */
>> >> + rank->v3.irouter[REG_RANK_INDEX(64,
>> >> + (gicd_reg - GICD_IROUTER), DABT_DOUBLE_WORD)] = *r;
>> >> + vgic_unlock_rank(v, rank, flags);
>> >> + return 1;
>> >> }
>> >> - vgic_lock_rank(v, rank, flags);
>> >> + irq = (gicd_reg - GICD_IROUTER)/8;
>> >> + /* IROUTER now specifies only one cpu affinity for this irq */
>> >> + /* Migrate from any core(vcpu0) to new_target */
>> >> + if ( (old_target & GICD_IROUTER_SPI_MODE_ANY) &&
>> >> + !(new_target & GICD_IROUTER_SPI_MODE_ANY) )
>> >> + {
>> >> + new_vcpu = vgicv3_irouter_to_vcpu(v, new_target);
>> >> + vgic_migrate_irq(v->domain->vcpu[0], new_vcpu, irq);
>> >> + }
>> >> + else
>> >> + {
>> >> + if ( old_target != new_target )
>> >> + {
>> >> + old_vcpu = vgicv3_irouter_to_vcpu(v, old_target);
>> >> + new_vcpu = vgicv3_irouter_to_vcpu(v, new_target);
>> >> + vgic_migrate_irq(old_vcpu, new_vcpu, irq);
>> >> + }
>> >> + }
>> >
>> > The issue is that if irouter is GICD_IROUTER_SPI_MODE_ANY, you always
>> > return vcpu0 below.
>> > So if the kernel:
>> >
>> > 1) move the irq to vcpu1
>> > 2) set the irq as GICD_IROUTER_SPI_MODE_ANY
>> >
>> > you now have migrated the irq to vcpu1 but you are returning vcpu0 from
>> > vgicv3_get_target_vcpu.
>> >
>> > You can either:
>> > - vgic_migrate_irq the irq back to vcpu0 when the kernel sets the irq as
>> > GICD_IROUTER_SPI_MODE_ANY
>>
>> When irq is set to vcpu1 and later when GICD_IROUTER_SPI_MODE_ANY
>> then I assume that vcpu1 is also valid for the moment if irq is pending.
>> So no need to do any migration.
>
> That would be OK, except that vgicv3_get_target_vcpu is actually
> returning vcpu0. We should try to match physical and virtual irq
> delivery as much as possible. So if the physical irq is delivered to
> pcpu1 (or whatever is the pcpu currently running vcpu1), then we should
> try hard to deliver the corresponding virq to vcpu1.
>
> Calling vgic_migrate_irq changes the physical irq affinity to the pcpu
> running the destination vcpu. So step 1 above moves physical irq
> delivery to pcpu1 (assuming that pcpu1 is running vcpu1 for simplicity).
> After the guest kernel sets the irq as GICD_IROUTER_SPI_MODE_ANY, even
> though we could deliver the irq to any vcpu, we should actually delivery
> the irq to the vcpu running on the pcpu that received the interrupt. In
> my example it would still be vcpu1. Therefore we need to return vcpu1
> from vgicv3_get_target_vcpu.
>
> Instead as it is now, we would cause an SGI to be sent from pcpu1 to
> pcpu0 in order to inject the irq to vcpu0 (assuming that vcpu0 is
> running on pcpu0 for simplicity).
>
>
>> Also, as per GICv3 spec, when GICD_IROUTER_SPI_MODE_ANY is set which is
>> previous not set ( 0 -> 1 ), the affinity value in IROUTER is
>> invalid/unknown.
>> So later when vgicv3_get_target_vcpu() is called, it always returns vcpu0.
>
> Could you please tell me which one is the relevant chapter of the GICv3
> spec? I couldn't find the statement you mentioned. Maybe my version of
> the spec is outdated.
See 20.0 version section 5.3.4
"When this bit is written from zero to one, the Affinity fields become UNKNOWN."
>
>
>> > - return the current vcpu for the irq rather than vcpu0 if
>> > GICD_IROUTER_SPI_MODE_ANY
>> >
>> > Either way it would work. Maybe the second option might be better.
>> >
>> >
>> >> rank->v3.irouter[REG_RANK_INDEX(64,
>> >> - (gicd_reg - GICD_IROUTER), DABT_DOUBLE_WORD)] = *r;
>> >> + (gicd_reg - GICD_IROUTER), DABT_DOUBLE_WORD)] =
>> >> new_target;
>> >> vgic_unlock_rank(v, rank, flags);
>> >> return 1;
>> >> case GICD_NSACR ... GICD_NSACRN:
>> >> @@ -928,6 +986,44 @@ static const struct mmio_handler_ops
>> >> vgic_distr_mmio_handler = {
>> >> .write_handler = vgic_v3_distr_mmio_write,
>> >> };
>> >>
>> >> +static struct vcpu *vgicv3_get_target_vcpu(struct vcpu *v, unsigned int
>> >> irq)
>> >
>> > for consistency I would call it vgic_v3_get_target_vcpu
>> >
>> >
>> >> +{
>> >> + int i;
>> >> + uint64_t irq_affinity, cpu_affinity;
>> >> + struct vcpu *v_target;
>> >> + struct vgic_irq_rank *rank = vgic_rank_irq(v, irq);
>> >> +
>> >> + ASSERT(spin_is_locked(&rank->lock));
>> >> +
>> >> + irq_affinity = rank->v3.irouter[irq % 32];
>> >> + if ( irq_affinity & GICD_IROUTER_SPI_MODE_ANY )
>> >> + {
>> >> + /*
>> >> + * IRQ routing mode set. Route any one processor in the entire
>> >> + * system. We chose vcpu 0
>> >> + */
>> >> + return v->domain->vcpu[0];
>> >> + }
>> >> +
>> >> + for ( i = 0; i < v->domain->max_vcpus; i++ )
>> >> + {
>> >> + v_target = v->domain->vcpu[i];
>> >> + cpu_affinity = (MPIDR_AFFINITY_LEVEL(v_target->arch.vmpidr, 3)
>> >> << 32 |
>> >> + MPIDR_AFFINITY_LEVEL(v_target->arch.vmpidr, 2)
>> >> << 16 |
>> >> + MPIDR_AFFINITY_LEVEL(v_target->arch.vmpidr, 1)
>> >> << 8 |
>> >> + MPIDR_AFFINITY_LEVEL(v_target->arch.vmpidr, 0));
>> >> +
>> >> + if ( irq_affinity == cpu_affinity )
>> >> + return v_target;
>> >> + }
>> >
>> > Same comment about the loop.
>> >
>> >
>> >> + gdprintk(XENLOG_WARNING,
>> >> + "d%d: No vcpu match found for irq %d with irouter 0x%lx\n",
>> >> + v->domain->domain_id, irq, irq_affinity );
>> >> +
>> >> + /* XXX:Fallback to vcpu0 ? */
>> >> + return v->domain->vcpu[0];
>> >
>> > This is wrong: only valid values should be allowed in rank->v3.irouter.
>> > If the kernel writes a bad value to IROUTER we should catch it in
>> > vgic_v3_distr_mmio_write and avoid saving it.
>>
>> 0x0 is also valid value. I found that VMPIDR for cpu0 is 0x80000000.
>> Bit[31] is RES1 bit which is set. If we ignore bit[31], 0x0 is also valid
>> value.
>>
>> Question: Shouldn't recover with wrong irouter value by choosing vcpu0?
>
> It is simpler and safer to avoid having any wrong irouter values in the
> first place. So for example we could write cpu0 to irouter in
> vgic_v3_distr_mmio_write if/when we catch an invalid guest write.
>
>
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |