[PATCH v3 19/19] KVM: arm64: ITS: Pending table save/restore
Auger Eric
eric.auger at redhat.com
Fri Mar 24 04:20:50 PDT 2017
Hi Andre,
On 22/03/2017 15:39, Andre Przywara wrote:
> Hi Eric,
>
> On 06/03/17 11:34, Eric Auger wrote:
>> Save and restore the pending tables.
>>
>> Pending table restore obviously requires the pendbaser to be
>> already set.
>>
>> Signed-off-by: Eric Auger <eric.auger at redhat.com>
>>
>> ---
>>
>> v1 -> v2:
>> - do not care about the 1st KB which should be zeroed according to
>> the spec.
>> ---
>> virt/kvm/arm/vgic/vgic-its.c | 71 ++++++++++++++++++++++++++++++++++++++++++--
>> 1 file changed, 69 insertions(+), 2 deletions(-)
>>
>> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
>> index 27ebabd..24824be 100644
>> --- a/virt/kvm/arm/vgic/vgic-its.c
>> +++ b/virt/kvm/arm/vgic/vgic-its.c
>> @@ -1736,7 +1736,48 @@ static int lookup_table(struct vgic_its *its, gpa_t base, int size, int esz,
>> */
>> static int vgic_its_flush_pending_tables(struct vgic_its *its)
>
> So as suspected before, I think passing the "its" pointer here is wrong.
> In fact you don't use that ITS except for getting the kvm pointer.
> Architecturally the pending tables are per redistributor, so you should
> pass a struct vcpu pointer.
> So the cleanest way would be to have a FLUSH/RESTORE_PENDING_TABLE ioctl
> on the *redistributor* kvm_device, which iterates through the list here
> and just dumps the pending bits for LPIs targeting that VCPU (skipping
> over others).
> Alternatively it would be enough to pass just a struct kvm pointer here
> and keep dumping all LPIs, but call this function only once per VM (not
> for each ITS). That sounds a bit dodgy from the architectural point of
> view, though.
>
>> {
>> - return -ENXIO;
>> + struct kvm *kvm = its->dev->kvm;
>> + struct vgic_dist *dist = &kvm->arch.vgic;
>> + struct vgic_irq *irq;
>> + int ret;
>> +
>> + /**
>> + * we do not take the dist->lpi_list_lock since we have a garantee
>> + * the LPI list is not touched while the its lock is held
>> + */
>
> As mentioned before I think this has to be reworked to take the lock,
> copy the table, drop the lock again and then iterate over the (private)
> copy to handle each LPI.
> Or something completely different.
> But IIRC the lpi_list_lock is taken completely independent of any ITS
> emulation code in vgic.c.
I aligned the code with other user access:
take the kvm lock and take all vcpu locks to make sure the vcpu are not
running
>
>> + list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
>> + struct kvm_vcpu *vcpu;
>> + gpa_t pendbase, ptr;
>> + bool stored;
>> + u8 val;
>> +
>> + vcpu = irq->target_vcpu;
>> + if (!vcpu)
>> + return -EINVAL;
>
> Isn't target_vcpu == NULL a valid use case? So continue; instead of return?
yes correct.
>
>> +
>> + pendbase = PENDBASER_ADDRESS(vcpu->arch.vgic_cpu.pendbaser);
>> +
>> + ptr = pendbase + (irq->intid / BITS_PER_BYTE);
>> +
>> + ret = kvm_read_guest(kvm, (gpa_t)ptr, &val, 1);
>> + if (ret)
>> + return ret;
>> +
>> + stored = val & (irq->intid % BITS_PER_BYTE);
>> + if (stored == irq->pending_latch)
>> + continue;
>> +
>> + if (irq->pending_latch)
>> + val |= 1 << (irq->intid % BITS_PER_BYTE);
>> + else
>> + val &= ~(1 << (irq->intid % BITS_PER_BYTE));
>> +
>> + ret = kvm_write_guest(kvm, (gpa_t)ptr, &val, 1);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + return 0;
>> }
>>
>> /**
>> @@ -1745,7 +1786,33 @@ static int vgic_its_flush_pending_tables(struct vgic_its *its)
>> */
>> static int vgic_its_restore_pending_tables(struct vgic_its *its)
>
> Could deserve the comment that it doesn't actually scan the table for
> set bits, but only checks the mapped LPIs (and thus should come last in
> the restore process).
> Also the same comment as above about using the "its" pointer applies here.
done
>
>> {
>> - return -ENXIO;
>> + struct vgic_irq *irq;
>> + struct kvm *kvm = its->dev->kvm;
>> + struct vgic_dist *dist = &kvm->arch.vgic;
>> + int ret;
>> +
>> + list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
>> + struct kvm_vcpu *vcpu;
>> + gpa_t pendbase, ptr;
>> + u8 val;
>> +
>> + vcpu = irq->target_vcpu;
>> + if (!vcpu)
>> + return -EINVAL;
>> +
>> + if (!(vcpu->arch.vgic_cpu.pendbaser & GICR_PENDBASER_PTZ))
>> + return 0;
>
> I believe this bit is only set once by software to communicate that
> *initially* (upon enabling LPIs in the redistributor) the pending table
> is clear. It is never cleared by the redistributor, so you can't rely on
> it here.
you're right
thanks
Eric
>
> Cheers,
> Andre.
>
>> +
>> + pendbase = PENDBASER_ADDRESS(vcpu->arch.vgic_cpu.pendbaser);
>> +
>> + ptr = pendbase + (irq->intid / BITS_PER_BYTE);
>> +
>> + ret = kvm_read_guest(kvm, (gpa_t)ptr, &val, 1);
>> + if (ret)
>> + return ret;
>> + irq->pending_latch = val & (1 << (irq->intid % BITS_PER_BYTE));
>> + }
>> + return 0;
>> }
>>
>> static int vgic_its_flush_ite(struct vgic_its *its, struct its_device *dev,
>>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
More information about the linux-arm-kernel
mailing list