diff mbox series

[v3,3/4] x86/nvmx: split updating RVI from SVI in nvmx_update_apicv

Message ID 20200326152720.36970-4-roger.pau@citrix.com (mailing list archive)
State Superseded
Headers show
Series x86/nvmx: fixes for interrupt injection | expand

Commit Message

Roger Pau Monné March 26, 2020, 3:27 p.m. UTC
Updating SVI is required when an interrupt has been injected using the
Ack on exit VMEXIT feature, so that the in service interrupt in the
GUEST_INTR_STATUS matches the vector that is signaled in
VM_EXIT_INTR_INFO.

Updating RVI however is not tied to the Ack on exit feature, as it
signals the next vector to be injected, and hence should always be
updated to the next pending vector, regardless of whether Ack on exit
is enabled.

When not using the Ack on exit feature preserve the previous vector in
SVI, so that it's not lost when RVI is updated to contain the pending
vector to inject.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
Changes since v2:
 - Return early if the exit reason != EXTERNAL_INTERRUPT.
 - Reduce the number of vmwrites by accumulating the changes to a
   local variable which is flushed at the end of the function.
 - Attempt to preserve the exiting SVI if Ack on exit is not enabled.
---
 xen/arch/x86/hvm/vmx/vvmx.c | 33 ++++++++++++++++++++++++---------
 1 file changed, 24 insertions(+), 9 deletions(-)

Comments

Tian, Kevin March 27, 2020, 2:21 a.m. UTC | #1
> From: Roger Pau Monne <roger.pau@citrix.com>
> Sent: Thursday, March 26, 2020 11:27 PM
> 
> Updating SVI is required when an interrupt has been injected using the
> Ack on exit VMEXIT feature, so that the in service interrupt in the
> GUEST_INTR_STATUS matches the vector that is signaled in
> VM_EXIT_INTR_INFO.
> 
> Updating RVI however is not tied to the Ack on exit feature, as it
> signals the next vector to be injected, and hence should always be
> updated to the next pending vector, regardless of whether Ack on exit
> is enabled.
> 
> When not using the Ack on exit feature preserve the previous vector in
> SVI, so that it's not lost when RVI is updated to contain the pending
> vector to inject.
> 
> Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>

Reviewed-by: Kevin Tian <kevin.tian@intel.com>, with one small comment:

> ---
> Changes since v2:
>  - Return early if the exit reason != EXTERNAL_INTERRUPT.
>  - Reduce the number of vmwrites by accumulating the changes to a
>    local variable which is flushed at the end of the function.
>  - Attempt to preserve the exiting SVI if Ack on exit is not enabled.
> ---
>  xen/arch/x86/hvm/vmx/vvmx.c | 33 ++++++++++++++++++++++++---------
>  1 file changed, 24 insertions(+), 9 deletions(-)
> 
> diff --git a/xen/arch/x86/hvm/vmx/vvmx.c b/xen/arch/x86/hvm/vmx/vvmx.c
> index 1753005c91..39fb553590 100644
> --- a/xen/arch/x86/hvm/vmx/vvmx.c
> +++ b/xen/arch/x86/hvm/vmx/vvmx.c
> @@ -1384,28 +1384,43 @@ static void nvmx_update_apicv(struct vcpu *v)
>      struct nestedvmx *nvmx = &vcpu_2_nvmx(v);
>      unsigned long reason = get_vvmcs(v, VM_EXIT_REASON);
>      unsigned long intr_info = get_vvmcs(v, VM_EXIT_INTR_INFO);
> +    unsigned long status;
> +    int rvi;
> 
> -    if ( reason == EXIT_REASON_EXTERNAL_INTERRUPT &&
> -         nvmx->intr.source == hvm_intsrc_lapic &&
> +    if ( reason != EXIT_REASON_EXTERNAL_INTERRUPT )
> +        return;

can we also exit if source is not lapic? as we discussed in another
thread, the whole logic here is only for lapic not others...

Thanks
Kevin

> +
> +    if ( nvmx->intr.source == hvm_intsrc_lapic &&
>           (intr_info & INTR_INFO_VALID_MASK) )
>      {
> -        uint16_t status;
> -        uint32_t rvi, ppr;
> -        uint32_t vector = intr_info & 0xff;
> +        uint32_t ppr;
> +        unsigned int vector = intr_info & INTR_INFO_VECTOR_MASK;
>          struct vlapic *vlapic = vcpu_vlapic(v);
> 
> +        /*
> +         * Update SVI to record the current in service interrupt that's
> +         * signaled in EXIT_INTR_INFO.
> +         */
>          vlapic_ack_pending_irq(v, vector, 1);
> 
>          ppr = vlapic_set_ppr(vlapic);
>          WARN_ON((ppr & 0xf0) != (vector & 0xf0));
> 
>          status = vector << VMX_GUEST_INTR_STATUS_SVI_OFFSET;
> -        rvi = vlapic_has_pending_irq(v);
> -        if ( rvi != -1 )
> -            status |= rvi & VMX_GUEST_INTR_STATUS_SUBFIELD_BITMASK;
> +    }
> +    else
> +       /* Keep previous SVI if there's any. */
> +       __vmread(GUEST_INTR_STATUS, &status);
> 
> -        __vmwrite(GUEST_INTR_STATUS, status);
> +    rvi = vlapic_has_pending_irq(v);
> +    if ( rvi != -1 )
> +    {
> +        status &= ~VMX_GUEST_INTR_STATUS_SUBFIELD_BITMASK
> +        status |= rvi & VMX_GUEST_INTR_STATUS_SUBFIELD_BITMASK;
>      }
> +
> +    if ( status )
> +        __vmwrite(GUEST_INTR_STATUS, status);
>  }
> 
>  static void virtual_vmexit(struct cpu_user_regs *regs)
> --
> 2.26.0
Roger Pau Monné March 27, 2020, 9:48 a.m. UTC | #2
On Fri, Mar 27, 2020 at 02:21:46AM +0000, Tian, Kevin wrote:
> > From: Roger Pau Monne <roger.pau@citrix.com>
> > Sent: Thursday, March 26, 2020 11:27 PM
> > 
> > Updating SVI is required when an interrupt has been injected using the
> > Ack on exit VMEXIT feature, so that the in service interrupt in the
> > GUEST_INTR_STATUS matches the vector that is signaled in
> > VM_EXIT_INTR_INFO.
> > 
> > Updating RVI however is not tied to the Ack on exit feature, as it
> > signals the next vector to be injected, and hence should always be
> > updated to the next pending vector, regardless of whether Ack on exit
> > is enabled.
> > 
> > When not using the Ack on exit feature preserve the previous vector in
> > SVI, so that it's not lost when RVI is updated to contain the pending
> > vector to inject.
> > 
> > Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
> 
> Reviewed-by: Kevin Tian <kevin.tian@intel.com>, with one small comment:
> 
> > ---
> > Changes since v2:
> >  - Return early if the exit reason != EXTERNAL_INTERRUPT.
> >  - Reduce the number of vmwrites by accumulating the changes to a
> >    local variable which is flushed at the end of the function.
> >  - Attempt to preserve the exiting SVI if Ack on exit is not enabled.
> > ---
> >  xen/arch/x86/hvm/vmx/vvmx.c | 33 ++++++++++++++++++++++++---------
> >  1 file changed, 24 insertions(+), 9 deletions(-)
> > 
> > diff --git a/xen/arch/x86/hvm/vmx/vvmx.c b/xen/arch/x86/hvm/vmx/vvmx.c
> > index 1753005c91..39fb553590 100644
> > --- a/xen/arch/x86/hvm/vmx/vvmx.c
> > +++ b/xen/arch/x86/hvm/vmx/vvmx.c
> > @@ -1384,28 +1384,43 @@ static void nvmx_update_apicv(struct vcpu *v)
> >      struct nestedvmx *nvmx = &vcpu_2_nvmx(v);
> >      unsigned long reason = get_vvmcs(v, VM_EXIT_REASON);
> >      unsigned long intr_info = get_vvmcs(v, VM_EXIT_INTR_INFO);
> > +    unsigned long status;
> > +    int rvi;
> > 
> > -    if ( reason == EXIT_REASON_EXTERNAL_INTERRUPT &&
> > -         nvmx->intr.source == hvm_intsrc_lapic &&
> > +    if ( reason != EXIT_REASON_EXTERNAL_INTERRUPT )
> > +        return;
> 
> can we also exit if source is not lapic? as we discussed in another
> thread, the whole logic here is only for lapic not others...

Right, in any case the code below will only update GUEST_INTR_STATUS
(either SVI or RVI) for lapic interrupts, but returning early if the
source is not the lapic will do no harm AFAICT.

Will send v4 with this changed.

Thanks, Roger.
diff mbox series

Patch

diff --git a/xen/arch/x86/hvm/vmx/vvmx.c b/xen/arch/x86/hvm/vmx/vvmx.c
index 1753005c91..39fb553590 100644
--- a/xen/arch/x86/hvm/vmx/vvmx.c
+++ b/xen/arch/x86/hvm/vmx/vvmx.c
@@ -1384,28 +1384,43 @@  static void nvmx_update_apicv(struct vcpu *v)
     struct nestedvmx *nvmx = &vcpu_2_nvmx(v);
     unsigned long reason = get_vvmcs(v, VM_EXIT_REASON);
     unsigned long intr_info = get_vvmcs(v, VM_EXIT_INTR_INFO);
+    unsigned long status;
+    int rvi;
 
-    if ( reason == EXIT_REASON_EXTERNAL_INTERRUPT &&
-         nvmx->intr.source == hvm_intsrc_lapic &&
+    if ( reason != EXIT_REASON_EXTERNAL_INTERRUPT )
+        return;
+
+    if ( nvmx->intr.source == hvm_intsrc_lapic &&
          (intr_info & INTR_INFO_VALID_MASK) )
     {
-        uint16_t status;
-        uint32_t rvi, ppr;
-        uint32_t vector = intr_info & 0xff;
+        uint32_t ppr;
+        unsigned int vector = intr_info & INTR_INFO_VECTOR_MASK;
         struct vlapic *vlapic = vcpu_vlapic(v);
 
+        /*
+         * Update SVI to record the current in service interrupt that's
+         * signaled in EXIT_INTR_INFO.
+         */
         vlapic_ack_pending_irq(v, vector, 1);
 
         ppr = vlapic_set_ppr(vlapic);
         WARN_ON((ppr & 0xf0) != (vector & 0xf0));
 
         status = vector << VMX_GUEST_INTR_STATUS_SVI_OFFSET;
-        rvi = vlapic_has_pending_irq(v);
-        if ( rvi != -1 )
-            status |= rvi & VMX_GUEST_INTR_STATUS_SUBFIELD_BITMASK;
+    }
+    else
+       /* Keep previous SVI if there's any. */
+       __vmread(GUEST_INTR_STATUS, &status);
 
-        __vmwrite(GUEST_INTR_STATUS, status);
+    rvi = vlapic_has_pending_irq(v);
+    if ( rvi != -1 )
+    {
+        status &= ~VMX_GUEST_INTR_STATUS_SUBFIELD_BITMASK
+        status |= rvi & VMX_GUEST_INTR_STATUS_SUBFIELD_BITMASK;
     }
+
+    if ( status )
+        __vmwrite(GUEST_INTR_STATUS, status);
 }
 
 static void virtual_vmexit(struct cpu_user_regs *regs)