[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH for-4.9 v2] hvm/vpt: fix inconsistent views of vIOAPIC in vmx_intr_assist()



> From: Gao, Chao
> Sent: Thursday, April 27, 2017 10:47 AM
> 
> When injecting periodic timer interrupt in vmx_intr_assist(),
> multi-read operations are done during one event delivery. For
> example, if a periodic timer interrupt is from PIT, when set the
> corresponding bit in vIRR, the corresponding RTE is accessed in
> pt_update_irq(). When this function returns, it accesses the RTE
> again to get the vector it sets in vIRR.  Between the two
> accesses, the content of RTE may have been changed by another CPU
> for no protection method in use. This case can incur the
> assertion failure in vmx_intr_assist().  Also after a periodic
> timer interrupt is injected successfully, pt_irq_posted() will
> decrease the count (pending_intr_nr). But if the corresponding
> RTE has been changed, pt_irq_posted() will not decrease the
> count, resulting one more timer interrupt.
> 
> More discussion and history can be found in
> 1.https://lists.xenproject.org/archives/html/xen-devel/2017-
> 03/msg00906.html
> 2.https://lists.xenproject.org/archives/html/xen-devel/2017-
> 01/msg01019.html
> 
> This patch introduces a new field 'latched_vector' to structure
> periodic_timer. The field is only valid between calling pt_update_irq()
> and calling pt_irq_posted() in vmx_intr_assist(). The new field is set
> to the vector we set in vIRR at the first access we describe above, is
> used in the following two accesses through calling pt_irq_vector() and
> finally cleared in pt_irq_posted() or updated in next calling
> vmx_intr_assist(). The latching operation should be protected by
> irq_lock to prevent other vCPUs changing the value we are interest in.
> Thus, provide a -locked variant of hvm_isa_irq_assert() to avoid
> deadlock.
> 
> Signed-off-by: Chao Gao <chao.gao@xxxxxxxxx>
> ---
> This patch is to fix a user triggerable assertion failure. I think it should
> be fixed in 4.9.
> 
> ---
> v2:
> - only use hold irq_lock in pt_update_irq(). Avoid holding irq_lock
> in vmx_intr_assist() to avoid putting too much functions in the
> locked-region.
> 
> ---
>  xen/arch/x86/hvm/irq.c        | 11 ++++++---
>  xen/arch/x86/hvm/vpt.c        | 56 +++++++++++++++++++++++++++-------------
> ---
>  xen/include/asm-x86/hvm/vpt.h |  6 +++++
>  xen/include/xen/hvm/irq.h     |  2 ++
>  4 files changed, 52 insertions(+), 23 deletions(-)
> 
> diff --git a/xen/arch/x86/hvm/irq.c b/xen/arch/x86/hvm/irq.c
> index 8625584..60deb6e 100644
> --- a/xen/arch/x86/hvm/irq.c
> +++ b/xen/arch/x86/hvm/irq.c
> @@ -126,20 +126,25 @@ void hvm_pci_intx_deassert(
>      spin_unlock(&d->arch.hvm_domain.irq_lock);
>  }
> 
> -void hvm_isa_irq_assert(
> +void hvm_isa_irq_assert_locked(
>      struct domain *d, unsigned int isa_irq)
>  {
>      struct hvm_irq *hvm_irq = hvm_domain_irq(d);
>      unsigned int gsi = hvm_isa_irq_to_gsi(isa_irq);
> 
>      ASSERT(isa_irq <= 15);
> -
> -    spin_lock(&d->arch.hvm_domain.irq_lock);
> +    ASSERT(spin_is_locked(&d->arch.hvm_domain.irq_lock));
> 
>      if ( !__test_and_set_bit(isa_irq, &hvm_irq->isa_irq.i) &&
>           (hvm_irq->gsi_assert_count[gsi]++ == 0) )
>          assert_irq(d, gsi, isa_irq);
> +}
> 
> +void hvm_isa_irq_assert(
> +    struct domain *d, unsigned int isa_irq)
> +{
> +    spin_lock(&d->arch.hvm_domain.irq_lock);
> +    hvm_isa_irq_assert_locked(d, isa_irq);
>      spin_unlock(&d->arch.hvm_domain.irq_lock);
>  }
> 
> diff --git a/xen/arch/x86/hvm/vpt.c b/xen/arch/x86/hvm/vpt.c
> index e3f2039..0edfc4a 100644
> --- a/xen/arch/x86/hvm/vpt.c
> +++ b/xen/arch/x86/hvm/vpt.c
> @@ -75,33 +75,43 @@ void hvm_set_guest_time(struct vcpu *v, u64
> guest_time)
>      }
>  }
> 
> -static int pt_irq_vector(struct periodic_time *pt, enum hvm_intsrc src)
> +static void pt_set_latched_vector(struct periodic_time *pt, enum
> hvm_intsrc src)
>  {
>      struct vcpu *v = pt->vcpu;
>      struct hvm_vioapic *vioapic;
> -    unsigned int gsi, isa_irq, pin;
> +    unsigned int gsi, pin;
> +
> +    ASSERT(pt->source == PTSRC_isa);
> +    ASSERT(src == hvm_intsrc_lapic);

Do we really need add such limitation here? Is it simpler to rename
original pt_irq_vector as pt_set_latched_vector by adding one
line to record returned value for all conditions and then define
pt_irq_vector simply as returning pt_latched_vector?

> +    gsi = hvm_isa_irq_to_gsi(pt->irq);
> +    vioapic = gsi_vioapic(v->domain, gsi, &pin);
> +    if ( !vioapic )
> +    {
> +        dprintk(XENLOG_WARNING, "d%u: invalid GSI (%u) for platform
> timer\n",
> +                v->domain->domain_id, gsi);
> +        domain_crash(v->domain);
> +        return;
> +    }
> +
> +    pt->latched_vector = vioapic->redirtbl[pin].fields.vector;
> +}
> +
> +static int pt_irq_vector(struct periodic_time *pt, enum hvm_intsrc src)
> +{
> +    struct vcpu *v = pt->vcpu;
> +    unsigned int isa_irq;
> 
>      if ( pt->source == PTSRC_lapic )
>          return pt->irq;
> 
>      isa_irq = pt->irq;
> -    gsi = hvm_isa_irq_to_gsi(isa_irq);
> 
>      if ( src == hvm_intsrc_pic )
>          return (v->domain->arch.hvm_domain.vpic[isa_irq >> 3].irq_base
>                  + (isa_irq & 7));
> 
>      ASSERT(src == hvm_intsrc_lapic);
> -    vioapic = gsi_vioapic(v->domain, gsi, &pin);
> -    if ( !vioapic )
> -    {
> -        dprintk(XENLOG_WARNING, "d%u: invalid GSI (%u) for platform
> timer\n",
> -                v->domain->domain_id, gsi);
> -        domain_crash(v->domain);
> -        return -1;
> -    }
> -
> -    return vioapic->redirtbl[pin].fields.vector;
> +    return pt->latched_vector;
>  }
> 
>  static int pt_irq_masked(struct periodic_time *pt)
> @@ -253,6 +263,7 @@ int pt_update_irq(struct vcpu *v)
>      struct periodic_time *pt, *temp, *earliest_pt;
>      uint64_t max_lag;
>      int irq, is_lapic;
> +    bool handle_by_pic = false;
> 
>      spin_lock(&v->arch.hvm_vcpu.tm_lock);
> 
> @@ -297,7 +308,15 @@ int pt_update_irq(struct vcpu *v)
>      else
>      {
>          hvm_isa_irq_deassert(v->domain, irq);
> -        hvm_isa_irq_assert(v->domain, irq);
> +        spin_lock(&v->domain->arch.hvm_domain.irq_lock);
> +        hvm_isa_irq_assert_locked(v->domain, irq);
> +        if ( platform_legacy_irq(irq) && vlapic_accept_pic_intr(v) &&
> +            (&v->domain->arch.hvm_domain)->vpic[irq >> 3].int_output )
> +        {
> +            handle_by_pic = true;
> +            pt_set_latched_vector(earliest_pt, hvm_intsrc_lapic);
> +        }

I think you changed the original semantics here. Originally -1
is returned when above condition is true otherwise pt_irq_vector
is returned. The otherwise condition is what you would like to
fix by latching vector. However your patch actually reverts
the policy.

> +        spin_unlock(&v->domain->arch.hvm_domain.irq_lock);
>      }
> 
>      /*
> @@ -305,12 +324,7 @@ int pt_update_irq(struct vcpu *v)
>       * IRR is returned and used to set eoi_exit_bitmap for virtual
>       * interrupt delivery case. Otherwise return -1 to do nothing.
>       */
> -    if ( !is_lapic &&
> -         platform_legacy_irq(irq) && vlapic_accept_pic_intr(v) &&
> -         (&v->domain->arch.hvm_domain)->vpic[irq >> 3].int_output )
> -        return -1;
> -    else
> -        return pt_irq_vector(earliest_pt, hvm_intsrc_lapic);
> +    return handle_by_pic ? -1 : pt_irq_vector(earliest_pt, hvm_intsrc_lapic);
>  }
> 
>  static struct periodic_time *is_pt_irq(
> @@ -347,6 +361,7 @@ void pt_intr_post(struct vcpu *v, struct hvm_intack
> intack)
>          return;
>      }
> 
> +    pt->latched_vector = -1;
>      pt->irq_issued = 0;
> 
>      if ( pt->one_shot )
> @@ -414,6 +429,7 @@ void create_periodic_time(
>      pt->pending_intr_nr = 0;
>      pt->do_not_freeze = 0;
>      pt->irq_issued = 0;
> +    pt->latched_vector = -1;
> 
>      /* Periodic timer must be at least 0.1ms. */
>      if ( (period < 100000) && period )
> diff --git a/xen/include/asm-x86/hvm/vpt.h b/xen/include/asm-
> x86/hvm/vpt.h
> index 21166ed..2daf356 100644
> --- a/xen/include/asm-x86/hvm/vpt.h
> +++ b/xen/include/asm-x86/hvm/vpt.h
> @@ -46,6 +46,12 @@ struct periodic_time {
>  #define PTSRC_lapic  2 /* LAPIC time source */
>      u8 source;                  /* PTSRC_ */
>      u8 irq;
> +    /*
> +     * Vector set in vIRR in one interrupt delivery. Using this value can
> +     * avoid reading the IOAPIC RTE again. Valid only when its source is
> +     * 'PTSRC_isa' and handled by vlapic.
> +     */
> +    int16_t latched_vector;
>      struct vcpu *vcpu;          /* vcpu timer interrupt delivers to */
>      u32 pending_intr_nr;        /* pending timer interrupts */
>      u64 period;                 /* frequency in ns */
> diff --git a/xen/include/xen/hvm/irq.h b/xen/include/xen/hvm/irq.h
> index 671a6f2..2451e8d 100644
> --- a/xen/include/xen/hvm/irq.h
> +++ b/xen/include/xen/hvm/irq.h
> @@ -120,6 +120,8 @@ void hvm_pci_intx_deassert(
>  /* Modify state of an ISA device's IRQ wire. */
>  void hvm_isa_irq_assert(
>      struct domain *d, unsigned int isa_irq);
> +void hvm_isa_irq_assert_locked(
> +    struct domain *d, unsigned int isa_irq);
>  void hvm_isa_irq_deassert(
>      struct domain *d, unsigned int isa_irq);
> 
> --
> 1.8.3.1


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.