diff mbox series

[RFC,6/6] x86/ioapic: mask entry while updating

Message ID 20220421132114.35118-7-roger.pau@citrix.com (mailing list archive)
State New, archived
Headers show
Series x86/ioapic: fix edge triggered interrupt migration | expand

Commit Message

Roger Pau Monné April 21, 2022, 1:21 p.m. UTC
When writing an IO-APIC RTE entry make sure incoming interrupts never
see a partially updated entry, by masking the pin while doing the
update when necessary.  Add some logic to attempt to limit the number
of writes.

With the masking now handled by __ioapic_write_entry itself when
necessary, we can drop the setting of the disable hook for IO-APIC
edge triggered interrupts.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
Note this includes a revert of the first patch in the series.
---
 xen/arch/x86/io_apic.c | 45 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 40 insertions(+), 5 deletions(-)

Comments

Jan Beulich July 5, 2022, 2:51 p.m. UTC | #1
On 21.04.2022 15:21, Roger Pau Monne wrote:
> --- a/xen/arch/x86/io_apic.c
> +++ b/xen/arch/x86/io_apic.c
> @@ -267,12 +267,47 @@ void __ioapic_write_entry(
>      unsigned int apic, unsigned int pin, bool raw,
>      struct IO_APIC_route_entry e)
>  {
> -    union entry_union eu = { .entry = e };
> -
>      if ( raw || !iommu_intremap )
>      {
> -        __io_apic_write(apic, 0x11 + 2 * pin, eu.w2);
> -        __io_apic_write(apic, 0x10 + 2 * pin, eu.w1);
> +        union entry_union eu = { .entry = e };
> +        union entry_union curr = {
> +            .entry = __ioapic_read_entry(apic, pin, true),
> +        };
> +        bool masked = true;
> +
> +        if ( curr.entry.mask )
> +        {
> +            /*
> +             * If pin is currently masked we can update the high part first
> +             * without worrying about the RTE being in an inconsistent state.
> +             */
> +            if ( curr.w2 != eu.w2 )
> +                __io_apic_write(apic, 0x11 + 2 * pin, eu.w2);
> +            if ( curr.w1 != eu.w1 )
> +                __io_apic_write(apic, 0x10 + 2 * pin, eu.w1);
> +            return;
> +        }
> +
> +        if ( curr.w1 != eu.w1 && curr.w2 != eu.w2 && !eu.entry.mask )
> +        {
> +            /*
> +             * If updating both halves mask the entry while updating so
> +             * interrupts are not injected with an inconsistent RTE.
> +             */
> +            eu.entry.mask = 1;
> +            masked = false;
> +        }
> +
> +        if ( curr.w1 != eu.w1 )
> +            __io_apic_write(apic, 0x10 + 2 * pin, eu.w1);
> +        if ( curr.w2 != eu.w2 )
> +            __io_apic_write(apic, 0x11 + 2 * pin, eu.w2);
> +
> +        if ( !masked )
> +        {
> +            eu.entry.mask = 0;
> +            __io_apic_write(apic, 0x10 + 2 * pin, eu.w1);
> +        }

For the write avoidance don't you want to hide differences in the
r/o delivery_status field, e.g. by setting curr's to eu's after
having read curr?

Jan
diff mbox series

Patch

diff --git a/xen/arch/x86/io_apic.c b/xen/arch/x86/io_apic.c
index f61e56f3d1..1860af7353 100644
--- a/xen/arch/x86/io_apic.c
+++ b/xen/arch/x86/io_apic.c
@@ -267,12 +267,47 @@  void __ioapic_write_entry(
     unsigned int apic, unsigned int pin, bool raw,
     struct IO_APIC_route_entry e)
 {
-    union entry_union eu = { .entry = e };
-
     if ( raw || !iommu_intremap )
     {
-        __io_apic_write(apic, 0x11 + 2 * pin, eu.w2);
-        __io_apic_write(apic, 0x10 + 2 * pin, eu.w1);
+        union entry_union eu = { .entry = e };
+        union entry_union curr = {
+            .entry = __ioapic_read_entry(apic, pin, true),
+        };
+        bool masked = true;
+
+        if ( curr.entry.mask )
+        {
+            /*
+             * If pin is currently masked we can update the high part first
+             * without worrying about the RTE being in an inconsistent state.
+             */
+            if ( curr.w2 != eu.w2 )
+                __io_apic_write(apic, 0x11 + 2 * pin, eu.w2);
+            if ( curr.w1 != eu.w1 )
+                __io_apic_write(apic, 0x10 + 2 * pin, eu.w1);
+            return;
+        }
+
+        if ( curr.w1 != eu.w1 && curr.w2 != eu.w2 && !eu.entry.mask )
+        {
+            /*
+             * If updating both halves mask the entry while updating so
+             * interrupts are not injected with an inconsistent RTE.
+             */
+            eu.entry.mask = 1;
+            masked = false;
+        }
+
+        if ( curr.w1 != eu.w1 )
+            __io_apic_write(apic, 0x10 + 2 * pin, eu.w1);
+        if ( curr.w2 != eu.w2 )
+            __io_apic_write(apic, 0x11 + 2 * pin, eu.w2);
+
+        if ( !masked )
+        {
+            eu.entry.mask = 0;
+            __io_apic_write(apic, 0x10 + 2 * pin, eu.w1);
+        }
     }
     else
         iommu_update_ire_from_apic(apic, pin, e.raw);
@@ -1780,7 +1815,7 @@  static hw_irq_controller ioapic_edge_type = {
     .startup 	= startup_edge_ioapic_irq,
     .shutdown 	= irq_shutdown_none,
     .enable 	= unmask_IO_APIC_irq,
-    .disable 	= mask_IO_APIC_irq,
+    .disable 	= irq_disable_none,
     .ack 		= ack_edge_ioapic_irq,
     .set_affinity 	= set_ioapic_affinity_irq,
 };