[PATCH v9 13/17] KVM: arm64: read initial LPI pending table

Andre Przywara andre.przywara at arm.com
Thu Jul 14 03:16:11 PDT 2016


Hi,

On 14/07/16 10:34, Marc Zyngier wrote:
> On 13/07/16 02:59, Andre Przywara wrote:
>> The LPI pending status for a GICv3 redistributor is held in a table
>> in (guest) memory. To achieve reasonable performance, we cache the
>> pending bit in our struct vgic_irq. The initial pending state must be
>> read from guest memory upon enabling LPIs for this redistributor.
>> As we can't access the guest memory while we hold the lpi_list spinlock,
>> we create a snapshot of the LPI list and iterate over that.
>>
>> Signed-off-by: Andre Przywara <andre.przywara at arm.com>
>> ---
>>  virt/kvm/arm/vgic/vgic-its.c | 91 ++++++++++++++++++++++++++++++++++++++++++++
>>  virt/kvm/arm/vgic/vgic.h     |  6 +++
>>  2 files changed, 97 insertions(+)
>>
>> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
>> index 4fc830c..f400ef1 100644
>> --- a/virt/kvm/arm/vgic/vgic-its.c
>> +++ b/virt/kvm/arm/vgic/vgic-its.c
>> @@ -63,6 +63,90 @@ struct its_itte {
>>  };
>>  
>>  #define CBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
>> +#define PENDBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 16))
>> +
>> +/*
>> + * Create a snapshot of the current LPI list, so that we can enumerate all
>> + * LPIs without holding any lock.
>> + * Returns the array length and puts the kmalloc'ed array into intid_ptr.
>> + */
>> +static int vgic_copy_lpi_list(struct kvm *kvm, u32 **intid_ptr)
>> +{
>> +	struct vgic_dist *dist = &kvm->arch.vgic;
>> +	struct vgic_irq *irq;
>> +	u32 *intids;
>> +	int irq_count = dist->lpi_list_count, i = 0;
>> +
>> +	/*
>> +	 * We use the current value of the list length, which may change
>> +	 * after the kmalloc. We don't care, because the guest shouldn't
>> +	 * change anything while the command handling is still running,
>> +	 * and in the worst case we would miss a new IRQ, which one wouldn't
>> +	 * expect to be covered by this command anyway.
>> +	 */
>> +	intids = kmalloc_array(irq_count, sizeof(intids[0]), GFP_KERNEL);
>> +	if (!intids)
>> +		return -ENOMEM;
>> +
>> +	spin_lock(&dist->lpi_list_lock);
>> +	list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
>> +		/* We don't need to "get" the IRQ, as we hold the list lock. */
>> +		intids[i] = irq->intid;
>> +		if (i++ == irq_count)
>> +			break;
> 
> So I can perfectly rewrite this as:
> 
> 		if (i == irq_count)
> 			break;
> 		i++;
> 
> Do you now see the bug and how you are performing out of bound accesses?

Ouch!

> 
>> +	}
>> +	spin_unlock(&dist->lpi_list_lock);
>> +
>> +	*intid_ptr = intids;
>> +	return irq_count;
>> +}
>> +
>> +/*
>> + * Scan the whole LPI pending table and sync the pending bit in there
>> + * with our own data structures. This relies on the LPI being
>> + * mapped before.
>> + */
>> +static int its_sync_lpi_pending_table(struct kvm_vcpu *vcpu)
>> +{
>> +	gpa_t pendbase = PENDBASER_ADDRESS(vcpu->arch.vgic_cpu.pendbaser);
>> +	struct vgic_irq *irq;
>> +	u8 pendmask;
>> +	int ret = 0;
>> +	u32 *intids;
>> +	int nr_irqs, i;
>> +
>> +	nr_irqs = vgic_copy_lpi_list(vcpu->kvm, &intids);
>> +	if (nr_irqs < 0)
>> +		return nr_irqs;
>> +
>> +	for (i = 0; i < nr_irqs; i++) {
>> +		int byte_offset, bit_nr;
>> +		int last_byte_offset = -1;
> 
> Nice try. But by keeping the last_byte_offset inside the loop, you've
> made sure that it is reset to -1 on each iteration. Wall <- head.

Ouch again.

Thanks for catching those! Fixed.

Cheers,
Andre.



More information about the linux-arm-kernel mailing list