All of lore.kernel.org
 help / color / mirror / Atom feed
From: Marc Zyngier <marc.zyngier@arm.com>
To: Andre Przywara <andre.przywara@arm.com>,
	Christoffer Dall <christoffer.dall@linaro.org>,
	Eric Auger <eric.auger@redhat.com>
Cc: kvmarm@lists.cs.columbia.edu, kvm@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org
Subject: Re: [PATCH v9 13/17] KVM: arm64: read initial LPI pending table
Date: Thu, 14 Jul 2016 10:34:10 +0100	[thread overview]
Message-ID: <57875C92.7000004@arm.com> (raw)
In-Reply-To: <20160713015909.28793-14-andre.przywara@arm.com>

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@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?

> +	}
> +	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.

> +
> +		byte_offset = intids[i] / BITS_PER_BYTE;
> +		bit_nr = intids[i] % BITS_PER_BYTE;
> +
> +		if (byte_offset != last_byte_offset) {
> +			ret = kvm_read_guest(vcpu->kvm, pendbase + byte_offset,
> +					     &pendmask, 1);
> +			if (ret) {
> +				kfree(intids);
> +				return ret;
> +			}
> +			last_byte_offset = byte_offset;
> +		}
> +
> +		irq = vgic_get_irq(vcpu->kvm, NULL, intids[i]);
> +		spin_lock(&irq->irq_lock);
> +		irq->pending = pendmask & (1U << bit_nr);
> +		vgic_queue_irq_unlock(vcpu->kvm, irq);
> +		vgic_put_irq(vcpu->kvm, irq);
> +	}
> +
> +	kfree(intids);
> +
> +	return ret;
> +}
>  
>  static unsigned long vgic_mmio_read_its_ctlr(struct kvm *vcpu,
>  					     struct vgic_its *its,
> @@ -406,6 +490,13 @@ static struct vgic_register_region its_registers[] = {
>  		VGIC_ACCESS_32bit),
>  };
>  
> +/* This is called on setting the LPI enable bit in the redistributor. */
> +void vgic_enable_lpis(struct kvm_vcpu *vcpu)
> +{
> +	if (!(vcpu->arch.vgic_cpu.pendbaser & GICR_PENDBASER_PTZ))
> +		its_sync_lpi_pending_table(vcpu);
> +}
> +
>  static int vgic_its_register(struct kvm *kvm, struct vgic_its *its)
>  {
>  	struct vgic_io_device *iodev = &its->iodev;
> diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
> index 9dc7207..32d8d82 100644
> --- a/virt/kvm/arm/vgic/vgic.h
> +++ b/virt/kvm/arm/vgic/vgic.h
> @@ -25,6 +25,7 @@
>  #define IS_VGIC_ADDR_UNDEF(_x)  ((_x) == VGIC_ADDR_UNDEF)
>  
>  #define INTERRUPT_ID_BITS_SPIS	10
> +#define INTERRUPT_ID_BITS_ITS	16
>  #define VGIC_PRI_BITS		5
>  
>  #define vgic_irq_is_sgi(intid) ((intid) < VGIC_NR_SGIS)
> @@ -77,6 +78,7 @@ int vgic_v3_map_resources(struct kvm *kvm);
>  int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t dist_base_address);
>  bool vgic_has_its(struct kvm *kvm);
>  int kvm_vgic_register_its_device(void);
> +void vgic_enable_lpis(struct kvm_vcpu *vcpu);
>  #else
>  static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu)
>  {
> @@ -138,6 +140,10 @@ static inline int kvm_vgic_register_its_device(void)
>  {
>  	return -ENODEV;
>  }
> +
> +static inline void vgic_enable_lpis(struct kvm_vcpu *vcpu)
> +{
> +}
>  #endif
>  
>  int kvm_register_vgic_device(unsigned long type);
> 

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

WARNING: multiple messages have this Message-ID (diff)
From: marc.zyngier@arm.com (Marc Zyngier)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v9 13/17] KVM: arm64: read initial LPI pending table
Date: Thu, 14 Jul 2016 10:34:10 +0100	[thread overview]
Message-ID: <57875C92.7000004@arm.com> (raw)
In-Reply-To: <20160713015909.28793-14-andre.przywara@arm.com>

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@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?

> +	}
> +	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.

> +
> +		byte_offset = intids[i] / BITS_PER_BYTE;
> +		bit_nr = intids[i] % BITS_PER_BYTE;
> +
> +		if (byte_offset != last_byte_offset) {
> +			ret = kvm_read_guest(vcpu->kvm, pendbase + byte_offset,
> +					     &pendmask, 1);
> +			if (ret) {
> +				kfree(intids);
> +				return ret;
> +			}
> +			last_byte_offset = byte_offset;
> +		}
> +
> +		irq = vgic_get_irq(vcpu->kvm, NULL, intids[i]);
> +		spin_lock(&irq->irq_lock);
> +		irq->pending = pendmask & (1U << bit_nr);
> +		vgic_queue_irq_unlock(vcpu->kvm, irq);
> +		vgic_put_irq(vcpu->kvm, irq);
> +	}
> +
> +	kfree(intids);
> +
> +	return ret;
> +}
>  
>  static unsigned long vgic_mmio_read_its_ctlr(struct kvm *vcpu,
>  					     struct vgic_its *its,
> @@ -406,6 +490,13 @@ static struct vgic_register_region its_registers[] = {
>  		VGIC_ACCESS_32bit),
>  };
>  
> +/* This is called on setting the LPI enable bit in the redistributor. */
> +void vgic_enable_lpis(struct kvm_vcpu *vcpu)
> +{
> +	if (!(vcpu->arch.vgic_cpu.pendbaser & GICR_PENDBASER_PTZ))
> +		its_sync_lpi_pending_table(vcpu);
> +}
> +
>  static int vgic_its_register(struct kvm *kvm, struct vgic_its *its)
>  {
>  	struct vgic_io_device *iodev = &its->iodev;
> diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
> index 9dc7207..32d8d82 100644
> --- a/virt/kvm/arm/vgic/vgic.h
> +++ b/virt/kvm/arm/vgic/vgic.h
> @@ -25,6 +25,7 @@
>  #define IS_VGIC_ADDR_UNDEF(_x)  ((_x) == VGIC_ADDR_UNDEF)
>  
>  #define INTERRUPT_ID_BITS_SPIS	10
> +#define INTERRUPT_ID_BITS_ITS	16
>  #define VGIC_PRI_BITS		5
>  
>  #define vgic_irq_is_sgi(intid) ((intid) < VGIC_NR_SGIS)
> @@ -77,6 +78,7 @@ int vgic_v3_map_resources(struct kvm *kvm);
>  int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t dist_base_address);
>  bool vgic_has_its(struct kvm *kvm);
>  int kvm_vgic_register_its_device(void);
> +void vgic_enable_lpis(struct kvm_vcpu *vcpu);
>  #else
>  static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu)
>  {
> @@ -138,6 +140,10 @@ static inline int kvm_vgic_register_its_device(void)
>  {
>  	return -ENODEV;
>  }
> +
> +static inline void vgic_enable_lpis(struct kvm_vcpu *vcpu)
> +{
> +}
>  #endif
>  
>  int kvm_register_vgic_device(unsigned long type);
> 

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

  reply	other threads:[~2016-07-14  9:34 UTC|newest]

Thread overview: 82+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
2016-07-13  1:58 ` Andre Przywara
2016-07-13  1:58 ` [PATCH v9 01/17] KVM: arm/arm64: move redistributor kvm_io_devices Andre Przywara
2016-07-13  1:58   ` Andre Przywara
2016-07-13  1:58 ` [PATCH v9 02/17] KVM: arm/arm64: check return value for kvm_register_vgic_device Andre Przywara
2016-07-13  1:58   ` Andre Przywara
2016-07-13  1:58 ` [PATCH v9 03/17] KVM: extend struct kvm_msi to hold a 32-bit device ID Andre Przywara
2016-07-13  1:58   ` Andre Przywara
2016-07-13 10:28   ` Paolo Bonzini
2016-07-13 10:28     ` Paolo Bonzini
2016-07-13  1:58 ` [PATCH v9 04/17] KVM: arm/arm64: extend arch CAP checks to allow per-VM capabilities Andre Przywara
2016-07-13  1:58   ` Andre Przywara
2016-07-13  1:58 ` [PATCH v9 05/17] KVM: kvm_io_bus: add kvm_io_bus_get_dev() call Andre Przywara
2016-07-13  1:58   ` Andre Przywara
2016-07-13 10:29   ` Paolo Bonzini
2016-07-13 10:29     ` Paolo Bonzini
2016-07-13  1:58 ` [PATCH v9 06/17] KVM: arm/arm64: VGIC: add refcounting for IRQs Andre Przywara
2016-07-13  1:58   ` Andre Przywara
2016-07-13 12:15   ` Marc Zyngier
2016-07-13 12:15     ` Marc Zyngier
2016-07-13 13:50     ` Andre Przywara
2016-07-13 13:50       ` Andre Przywara
2016-07-13  1:58 ` [PATCH v9 07/17] irqchip: refactor and add GICv3 definitions Andre Przywara
2016-07-13  1:58   ` Andre Przywara
2016-07-13 12:28   ` Marc Zyngier
2016-07-13 12:28     ` Marc Zyngier
2016-07-13  1:59 ` [PATCH v9 08/17] KVM: arm64: handle ITS related GICv3 redistributor registers Andre Przywara
2016-07-13  1:59   ` Andre Przywara
2016-07-13  1:59 ` [PATCH v9 09/17] KVM: arm64: introduce ITS emulation file with MMIO framework Andre Przywara
2016-07-13  1:59   ` Andre Przywara
2016-07-13  1:59 ` [PATCH v9 10/17] KVM: arm64: introduce new KVM ITS device Andre Przywara
2016-07-13  1:59   ` Andre Przywara
2016-07-14  8:36   ` Marc Zyngier
2016-07-14  8:36     ` Marc Zyngier
2016-07-14 11:11     ` Andre Przywara
2016-07-14 11:11       ` Andre Przywara
2016-07-14 12:57       ` Marc Zyngier
2016-07-14 12:57         ` Marc Zyngier
2016-07-15  9:33     ` Andre Przywara
2016-07-15  9:33       ` Andre Przywara
2016-07-13  1:59 ` [PATCH v9 11/17] KVM: arm64: implement basic ITS register handlers Andre Przywara
2016-07-13  1:59   ` Andre Przywara
2016-07-14  9:13   ` Marc Zyngier
2016-07-14  9:13     ` Marc Zyngier
2016-07-13  1:59 ` [PATCH v9 12/17] KVM: arm64: connect LPIs to the VGIC emulation Andre Przywara
2016-07-13  1:59   ` Andre Przywara
2016-07-13  1:59 ` [PATCH v9 13/17] KVM: arm64: read initial LPI pending table Andre Przywara
2016-07-13  1:59   ` Andre Przywara
2016-07-14  9:34   ` Marc Zyngier [this message]
2016-07-14  9:34     ` Marc Zyngier
2016-07-14 10:16     ` Andre Przywara
2016-07-14 10:16       ` Andre Przywara
2016-07-13  1:59 ` [PATCH v9 14/17] KVM: arm64: allow updates of LPI configuration table Andre Przywara
2016-07-13  1:59   ` Andre Przywara
2016-07-14  9:46   ` Marc Zyngier
2016-07-14  9:46     ` Marc Zyngier
2016-07-14 10:00     ` Andre Przywara
2016-07-14 10:00       ` Andre Przywara
2016-07-14 10:10       ` Marc Zyngier
2016-07-14 10:10         ` Marc Zyngier
2016-07-13  1:59 ` [PATCH v9 15/17] KVM: arm64: implement ITS command queue command handlers Andre Przywara
2016-07-13  1:59   ` Andre Przywara
2016-07-14 10:38   ` Marc Zyngier
2016-07-14 10:38     ` Marc Zyngier
2016-07-14 15:35     ` Andre Przywara
2016-07-14 15:35       ` Andre Przywara
2016-07-14 16:33       ` Marc Zyngier
2016-07-14 16:33         ` Marc Zyngier
2016-07-15  8:19         ` Marc Zyngier
2016-07-15  8:19           ` Marc Zyngier
2016-07-13  1:59 ` [PATCH v9 16/17] KVM: arm64: implement MSI injection in ITS emulation Andre Przywara
2016-07-13  1:59   ` Andre Przywara
2016-07-13  1:59 ` [PATCH v9 17/17] KVM: arm64: enable ITS emulation as a virtual MSI controller Andre Przywara
2016-07-13  1:59   ` Andre Przywara
2016-07-13  7:57 ` [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Diana Madalina Craciun
2016-07-13  7:57   ` Diana Madalina Craciun
2016-07-13  8:15   ` Andre Przywara
2016-07-13  8:15     ` Andre Przywara
2016-07-14 10:52 ` Marc Zyngier
2016-07-14 10:52   ` Marc Zyngier
2016-07-14 11:01   ` Paolo Bonzini
2016-07-14 11:01     ` Paolo Bonzini

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=57875C92.7000004@arm.com \
    --to=marc.zyngier@arm.com \
    --cc=andre.przywara@arm.com \
    --cc=christoffer.dall@linaro.org \
    --cc=eric.auger@redhat.com \
    --cc=kvm@vger.kernel.org \
    --cc=kvmarm@lists.cs.columbia.edu \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.