diff mbox series

[3/3] x86/nvmx: update exit bitmap on vmexit

Message ID 20200320190737.42110-4-roger.pau@citrix.com (mailing list archive)
State New, archived
Headers show
Series x86/nvmx: attempt to fix interrupt injection | expand

Commit Message

Roger Pau Monne March 20, 2020, 7:07 p.m. UTC
Current code in nvmx_update_apicv set the guest interrupt status field
but doesn't update the exit bitmap, which can cause issues of lost
interrupts on the L1 hypervisor if vmx_intr_assist gets
short-circuited by nvmx_intr_intercept returning true.

Extract the code to update the exit bitmap from vmx_intr_assist into a
helper and use it in nvmx_update_apicv when updating the guest
interrupt status field.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
 xen/arch/x86/hvm/vmx/intr.c       | 21 +++++++++++++--------
 xen/arch/x86/hvm/vmx/vvmx.c       |  1 +
 xen/include/asm-x86/hvm/vmx/vmx.h |  2 ++
 3 files changed, 16 insertions(+), 8 deletions(-)

Comments

Tian, Kevin March 24, 2020, 6:22 a.m. UTC | #1
> From: Roger Pau Monne <roger.pau@citrix.com>
> Sent: Saturday, March 21, 2020 3:08 AM
> 
> Current code in nvmx_update_apicv set the guest interrupt status field
> but doesn't update the exit bitmap, which can cause issues of lost
> interrupts on the L1 hypervisor if vmx_intr_assist gets
> short-circuited by nvmx_intr_intercept returning true.

Above is not accurate. Currently Xen didn't choose to update the EOI
exit bitmap every time when there is a change. Instead, it chose to 
batch the update before resuming to the guest. sort of optimization.
So it is not related to whether SVI is changed. We should always do the 
bitmap update in nvmx_update_apicv, regardless of the setting of
Ack-on-exit ...

Thanks
Kevin

> 
> Extract the code to update the exit bitmap from vmx_intr_assist into a
> helper and use it in nvmx_update_apicv when updating the guest
> interrupt status field.
> 
> Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
> ---
>  xen/arch/x86/hvm/vmx/intr.c       | 21 +++++++++++++--------
>  xen/arch/x86/hvm/vmx/vvmx.c       |  1 +
>  xen/include/asm-x86/hvm/vmx/vmx.h |  2 ++
>  3 files changed, 16 insertions(+), 8 deletions(-)
> 
> diff --git a/xen/arch/x86/hvm/vmx/intr.c b/xen/arch/x86/hvm/vmx/intr.c
> index 49a1295f09..000e14af49 100644
> --- a/xen/arch/x86/hvm/vmx/intr.c
> +++ b/xen/arch/x86/hvm/vmx/intr.c
> @@ -224,6 +224,18 @@ static int nvmx_intr_intercept(struct vcpu *v, struct
> hvm_intack intack)
>      return 0;
>  }
> 
> +void vmx_sync_exit_bitmap(struct vcpu *v)
> +{
> +    const unsigned int n = ARRAY_SIZE(v->arch.hvm.vmx.eoi_exit_bitmap);
> +    unsigned int i;
> +
> +    while ( (i = find_first_bit(&v->arch.hvm.vmx.eoi_exitmap_changed, n)) <
> n )
> +    {
> +        clear_bit(i, &v->arch.hvm.vmx.eoi_exitmap_changed);
> +        __vmwrite(EOI_EXIT_BITMAP(i), v->arch.hvm.vmx.eoi_exit_bitmap[i]);
> +    }
> +}
> +
>  void vmx_intr_assist(void)
>  {
>      struct hvm_intack intack;
> @@ -318,7 +330,6 @@ void vmx_intr_assist(void)
>                intack.source != hvm_intsrc_vector )
>      {
>          unsigned long status;
> -        unsigned int i, n;
> 
>         /*
>          * intack.vector is the highest priority vector. So we set eoi_exit_bitmap
> @@ -379,13 +390,7 @@ void vmx_intr_assist(void)
>                      intack.vector;
>          __vmwrite(GUEST_INTR_STATUS, status);
> 
> -        n = ARRAY_SIZE(v->arch.hvm.vmx.eoi_exit_bitmap);
> -        while ( (i = find_first_bit(&v->arch.hvm.vmx.eoi_exitmap_changed,
> -                                    n)) < n )
> -        {
> -            clear_bit(i, &v->arch.hvm.vmx.eoi_exitmap_changed);
> -            __vmwrite(EOI_EXIT_BITMAP(i), v->arch.hvm.vmx.eoi_exit_bitmap[i]);
> -        }
> +        vmx_sync_exit_bitmap(v);
> 
>          pt_intr_post(v, intack);
>      }
> diff --git a/xen/arch/x86/hvm/vmx/vvmx.c b/xen/arch/x86/hvm/vmx/vvmx.c
> index 180d01e385..e041ecc115 100644
> --- a/xen/arch/x86/hvm/vmx/vvmx.c
> +++ b/xen/arch/x86/hvm/vmx/vvmx.c
> @@ -1414,6 +1414,7 @@ static void nvmx_update_apicv(struct vcpu *v)
>              status |= rvi & VMX_GUEST_INTR_STATUS_SUBFIELD_BITMASK;
> 
>          __vmwrite(GUEST_INTR_STATUS, status);
> +        vmx_sync_exit_bitmap(v);
>      }
>  }
> 
> diff --git a/xen/include/asm-x86/hvm/vmx/vmx.h b/xen/include/asm-
> x86/hvm/vmx/vmx.h
> index b334e1ec94..111ccd7e61 100644
> --- a/xen/include/asm-x86/hvm/vmx/vmx.h
> +++ b/xen/include/asm-x86/hvm/vmx/vmx.h
> @@ -610,6 +610,8 @@ void update_guest_eip(void);
>  void vmx_pi_per_cpu_init(unsigned int cpu);
>  void vmx_pi_desc_fixup(unsigned int cpu);
> 
> +void vmx_sync_exit_bitmap(struct vcpu *v);
> +
>  #ifdef CONFIG_HVM
>  void vmx_pi_hooks_assign(struct domain *d);
>  void vmx_pi_hooks_deassign(struct domain *d);
> --
> 2.25.0
Roger Pau Monne March 24, 2020, 9:59 a.m. UTC | #2
On Tue, Mar 24, 2020 at 06:22:43AM +0000, Tian, Kevin wrote:
> > From: Roger Pau Monne <roger.pau@citrix.com>
> > Sent: Saturday, March 21, 2020 3:08 AM
> > 
> > Current code in nvmx_update_apicv set the guest interrupt status field
> > but doesn't update the exit bitmap, which can cause issues of lost
> > interrupts on the L1 hypervisor if vmx_intr_assist gets
> > short-circuited by nvmx_intr_intercept returning true.
> 
> Above is not accurate. Currently Xen didn't choose to update the EOI
> exit bitmap every time when there is a change. Instead, it chose to 
> batch the update before resuming to the guest. sort of optimization.
> So it is not related to whether SVI is changed. We should always do the 
> bitmap update in nvmx_update_apicv, regardless of the setting of
> Ack-on-exit ...

But if Ack on exit is not set the GUEST_INTR_STATUS won't be changed
by nvmx_intr_intercept, and hence there's no need to update the EOI
exit map?

If OTOH the GUEST_INTR_STATUS field is changed by vmx_intr_assist the
bitmap will already be updated there.

Thanks, Roger.
Tian, Kevin March 24, 2020, 10:16 a.m. UTC | #3
> From: Roger Pau Monné <roger.pau@citrix.com>
> Sent: Tuesday, March 24, 2020 5:59 PM
> 
> On Tue, Mar 24, 2020 at 06:22:43AM +0000, Tian, Kevin wrote:
> > > From: Roger Pau Monne <roger.pau@citrix.com>
> > > Sent: Saturday, March 21, 2020 3:08 AM
> > >
> > > Current code in nvmx_update_apicv set the guest interrupt status field
> > > but doesn't update the exit bitmap, which can cause issues of lost
> > > interrupts on the L1 hypervisor if vmx_intr_assist gets
> > > short-circuited by nvmx_intr_intercept returning true.
> >
> > Above is not accurate. Currently Xen didn't choose to update the EOI
> > exit bitmap every time when there is a change. Instead, it chose to
> > batch the update before resuming to the guest. sort of optimization.
> > So it is not related to whether SVI is changed. We should always do the
> > bitmap update in nvmx_update_apicv, regardless of the setting of
> > Ack-on-exit ...
> 
> But if Ack on exit is not set the GUEST_INTR_STATUS won't be changed
> by nvmx_intr_intercept, and hence there's no need to update the EOI
> exit map?
> 
> If OTOH the GUEST_INTR_STATUS field is changed by vmx_intr_assist the
> bitmap will already be updated there.
> 

If you agree with my comment in patch 2/3 about setting RVI for 
ack-on-exit=0, then EOI bitmap update should be done there too.

Thanks
Kevin
Roger Pau Monne March 24, 2020, 11:24 a.m. UTC | #4
On Tue, Mar 24, 2020 at 10:16:52AM +0000, Tian, Kevin wrote:
> > From: Roger Pau Monné <roger.pau@citrix.com>
> > Sent: Tuesday, March 24, 2020 5:59 PM
> > 
> > On Tue, Mar 24, 2020 at 06:22:43AM +0000, Tian, Kevin wrote:
> > > > From: Roger Pau Monne <roger.pau@citrix.com>
> > > > Sent: Saturday, March 21, 2020 3:08 AM
> > > >
> > > > Current code in nvmx_update_apicv set the guest interrupt status field
> > > > but doesn't update the exit bitmap, which can cause issues of lost
> > > > interrupts on the L1 hypervisor if vmx_intr_assist gets
> > > > short-circuited by nvmx_intr_intercept returning true.
> > >
> > > Above is not accurate. Currently Xen didn't choose to update the EOI
> > > exit bitmap every time when there is a change. Instead, it chose to
> > > batch the update before resuming to the guest. sort of optimization.
> > > So it is not related to whether SVI is changed. We should always do the
> > > bitmap update in nvmx_update_apicv, regardless of the setting of
> > > Ack-on-exit ...
> > 
> > But if Ack on exit is not set the GUEST_INTR_STATUS won't be changed
> > by nvmx_intr_intercept, and hence there's no need to update the EOI
> > exit map?
> > 
> > If OTOH the GUEST_INTR_STATUS field is changed by vmx_intr_assist the
> > bitmap will already be updated there.
> > 
> 
> If you agree with my comment in patch 2/3 about setting RVI for 
> ack-on-exit=0, then EOI bitmap update should be done there too.

Yes, I agree, see my reply to your comment. I (again) misremembered the
sequence and assumed the switch will happen prior to calling
vmx_intr_assist which is not the case.

Thanks, Roger.
diff mbox series

Patch

diff --git a/xen/arch/x86/hvm/vmx/intr.c b/xen/arch/x86/hvm/vmx/intr.c
index 49a1295f09..000e14af49 100644
--- a/xen/arch/x86/hvm/vmx/intr.c
+++ b/xen/arch/x86/hvm/vmx/intr.c
@@ -224,6 +224,18 @@  static int nvmx_intr_intercept(struct vcpu *v, struct hvm_intack intack)
     return 0;
 }
 
+void vmx_sync_exit_bitmap(struct vcpu *v)
+{
+    const unsigned int n = ARRAY_SIZE(v->arch.hvm.vmx.eoi_exit_bitmap);
+    unsigned int i;
+
+    while ( (i = find_first_bit(&v->arch.hvm.vmx.eoi_exitmap_changed, n)) < n )
+    {
+        clear_bit(i, &v->arch.hvm.vmx.eoi_exitmap_changed);
+        __vmwrite(EOI_EXIT_BITMAP(i), v->arch.hvm.vmx.eoi_exit_bitmap[i]);
+    }
+}
+
 void vmx_intr_assist(void)
 {
     struct hvm_intack intack;
@@ -318,7 +330,6 @@  void vmx_intr_assist(void)
               intack.source != hvm_intsrc_vector )
     {
         unsigned long status;
-        unsigned int i, n;
 
        /*
         * intack.vector is the highest priority vector. So we set eoi_exit_bitmap
@@ -379,13 +390,7 @@  void vmx_intr_assist(void)
                     intack.vector;
         __vmwrite(GUEST_INTR_STATUS, status);
 
-        n = ARRAY_SIZE(v->arch.hvm.vmx.eoi_exit_bitmap);
-        while ( (i = find_first_bit(&v->arch.hvm.vmx.eoi_exitmap_changed,
-                                    n)) < n )
-        {
-            clear_bit(i, &v->arch.hvm.vmx.eoi_exitmap_changed);
-            __vmwrite(EOI_EXIT_BITMAP(i), v->arch.hvm.vmx.eoi_exit_bitmap[i]);
-        }
+        vmx_sync_exit_bitmap(v);
 
         pt_intr_post(v, intack);
     }
diff --git a/xen/arch/x86/hvm/vmx/vvmx.c b/xen/arch/x86/hvm/vmx/vvmx.c
index 180d01e385..e041ecc115 100644
--- a/xen/arch/x86/hvm/vmx/vvmx.c
+++ b/xen/arch/x86/hvm/vmx/vvmx.c
@@ -1414,6 +1414,7 @@  static void nvmx_update_apicv(struct vcpu *v)
             status |= rvi & VMX_GUEST_INTR_STATUS_SUBFIELD_BITMASK;
 
         __vmwrite(GUEST_INTR_STATUS, status);
+        vmx_sync_exit_bitmap(v);
     }
 }
 
diff --git a/xen/include/asm-x86/hvm/vmx/vmx.h b/xen/include/asm-x86/hvm/vmx/vmx.h
index b334e1ec94..111ccd7e61 100644
--- a/xen/include/asm-x86/hvm/vmx/vmx.h
+++ b/xen/include/asm-x86/hvm/vmx/vmx.h
@@ -610,6 +610,8 @@  void update_guest_eip(void);
 void vmx_pi_per_cpu_init(unsigned int cpu);
 void vmx_pi_desc_fixup(unsigned int cpu);
 
+void vmx_sync_exit_bitmap(struct vcpu *v);
+
 #ifdef CONFIG_HVM
 void vmx_pi_hooks_assign(struct domain *d);
 void vmx_pi_hooks_deassign(struct domain *d);