Patchwork [RFT,08/15] x86: Refactor RTC IRQ coalescing workaround

login
register
mail settings
Submitter Jan Kiszka
Date May 24, 2010, 8:13 p.m.
Message ID <44bf86c07e89667d264c6271f2c3ee5a35696cce.1274732025.git.jan.kiszka@web.de>
Download mbox | patch
Permalink /patch/53469/
State New
Headers show

Comments

Jan Kiszka - May 24, 2010, 8:13 p.m.
From: Jan Kiszka <jan.kiszka@siemens.com>

Make use of the new feedback IRQ handlers and propagate coalesced
deliveries via handler return code from the sink to the source. As a
by-product, this also adds coalescing support to the PIC.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
 hw/apic.c        |   61 ++++++++++++++++++++++++++---------------------------
 hw/apic.h        |   10 ++------
 hw/i8259.c       |   20 +++++++++++++++--
 hw/ioapic.c      |   34 ++++++++++++++++++-----------
 hw/mc146818rtc.c |   22 ++++++++-----------
 hw/pc.c          |   17 ++++++++++----
 hw/pc.h          |    2 +-
 hw/pc_piix.c     |    2 +-
 8 files changed, 94 insertions(+), 74 deletions(-)

Patch

diff --git a/hw/apic.c b/hw/apic.c
index 9029dad..641825c 100644
--- a/hw/apic.c
+++ b/hw/apic.c
@@ -108,10 +108,8 @@  typedef struct APICState {
 static int apic_io_memory;
 static APICState *local_apics[MAX_APICS + 1];
 static int last_apic_idx = 0;
-static int apic_irq_delivered;
 
-
-static void apic_set_irq(APICState *s, int vector_num, int trigger_mode);
+static int apic_set_irq(APICState *s, int vector_num, int trigger_mode);
 static void apic_update_irq(APICState *s);
 static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask,
                                       uint8_t dest, uint8_t dest_mode);
@@ -222,12 +220,12 @@  void apic_deliver_pic_intr(CPUState *env, int level)
     }\
 }
 
-static void apic_bus_deliver(const uint32_t *deliver_bitmask,
-                             uint8_t delivery_mode,
-                             uint8_t vector_num, uint8_t polarity,
-                             uint8_t trigger_mode)
+static int apic_bus_deliver(const uint32_t *deliver_bitmask,
+                            uint8_t delivery_mode, uint8_t vector_num,
+                            uint8_t polarity, uint8_t trigger_mode)
 {
     APICState *apic_iter;
+    int ret;
 
     switch (delivery_mode) {
         case APIC_DM_LOWPRI:
@@ -244,11 +242,12 @@  static void apic_bus_deliver(const uint32_t *deliver_bitmask,
                 if (d >= 0) {
                     apic_iter = local_apics[d];
                     if (apic_iter) {
-                        apic_set_irq(apic_iter, vector_num, trigger_mode);
+                        return apic_set_irq(apic_iter, vector_num,
+                                            trigger_mode);
                     }
                 }
             }
-            return;
+            return QEMU_IRQ_MASKED;
 
         case APIC_DM_FIXED:
             break;
@@ -256,40 +255,48 @@  static void apic_bus_deliver(const uint32_t *deliver_bitmask,
         case APIC_DM_SMI:
             foreach_apic(apic_iter, deliver_bitmask,
                 cpu_interrupt(apic_iter->cpu_env, CPU_INTERRUPT_SMI) );
-            return;
+            return QEMU_IRQ_DELIVERED;
 
         case APIC_DM_NMI:
             foreach_apic(apic_iter, deliver_bitmask,
                 cpu_interrupt(apic_iter->cpu_env, CPU_INTERRUPT_NMI) );
-            return;
+            return QEMU_IRQ_DELIVERED;
 
         case APIC_DM_INIT:
             /* normal INIT IPI sent to processors */
             foreach_apic(apic_iter, deliver_bitmask,
                          cpu_interrupt(apic_iter->cpu_env, CPU_INTERRUPT_INIT) );
-            return;
+            return QEMU_IRQ_DELIVERED;
 
         case APIC_DM_EXTINT:
             /* handled in I/O APIC code */
             break;
 
         default:
-            return;
+            return QEMU_IRQ_MASKED;
     }
 
+    ret = QEMU_IRQ_MASKED;
     foreach_apic(apic_iter, deliver_bitmask,
-                 apic_set_irq(apic_iter, vector_num, trigger_mode) );
+        if (ret == QEMU_IRQ_MASKED)
+            ret = QEMU_IRQ_COALESCED;
+        if (apic_set_irq(apic_iter, vector_num,
+                         trigger_mode) == QEMU_IRQ_DELIVERED) {
+            ret = QEMU_IRQ_DELIVERED;
+        }
+    );
+    return ret;
 }
 
-void apic_deliver_irq(uint8_t dest, uint8_t dest_mode,
-                      uint8_t delivery_mode, uint8_t vector_num,
-                      uint8_t polarity, uint8_t trigger_mode)
+int apic_deliver_irq(uint8_t dest, uint8_t dest_mode,
+                     uint8_t delivery_mode, uint8_t vector_num,
+                     uint8_t polarity, uint8_t trigger_mode)
 {
     uint32_t deliver_bitmask[MAX_APIC_WORDS];
 
     apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode);
-    apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, polarity,
-                     trigger_mode);
+    return apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num,
+                            polarity, trigger_mode);
 }
 
 void cpu_set_apic_base(CPUState *env, uint64_t val)
@@ -384,19 +391,10 @@  static void apic_update_irq(APICState *s)
     cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
 }
 
-void apic_reset_irq_delivered(void)
-{
-    apic_irq_delivered = 0;
-}
-
-int apic_get_irq_delivered(void)
-{
-    return apic_irq_delivered;
-}
-
-static void apic_set_irq(APICState *s, int vector_num, int trigger_mode)
+static int apic_set_irq(APICState *s, int vector_num, int trigger_mode)
 {
-    apic_irq_delivered += !get_bit(s->irr, vector_num);
+    int ret = get_bit(s->irr, vector_num) ? QEMU_IRQ_COALESCED
+                                          : QEMU_IRQ_DELIVERED;
 
     set_bit(s->irr, vector_num);
     if (trigger_mode)
@@ -404,6 +402,7 @@  static void apic_set_irq(APICState *s, int vector_num, int trigger_mode)
     else
         reset_bit(s->tmr, vector_num);
     apic_update_irq(s);
+    return ret;
 }
 
 static void apic_eoi(APICState *s)
diff --git a/hw/apic.h b/hw/apic.h
index 132fcab..738d98a 100644
--- a/hw/apic.h
+++ b/hw/apic.h
@@ -2,18 +2,14 @@ 
 #define APIC_H
 
 typedef struct IOAPICState IOAPICState;
-void apic_deliver_irq(uint8_t dest, uint8_t dest_mode,
-                             uint8_t delivery_mode,
-                             uint8_t vector_num, uint8_t polarity,
-                             uint8_t trigger_mode);
+int apic_deliver_irq(uint8_t dest, uint8_t dest_mode,
+                     uint8_t delivery_mode, uint8_t vector_num,
+                     uint8_t polarity, uint8_t trigger_mode);
 int apic_init(CPUState *env);
 int apic_accept_pic_intr(CPUState *env);
 void apic_deliver_pic_intr(CPUState *env, int level);
 int apic_get_interrupt(CPUState *env);
 qemu_irq *ioapic_init(void);
-void ioapic_set_irq(void *opaque, int vector, int level);
-void apic_reset_irq_delivered(void);
-int apic_get_irq_delivered(void);
 
 int cpu_is_bsp(CPUState *env);
 
diff --git a/hw/i8259.c b/hw/i8259.c
index ea48e0e..c05adf2 100644
--- a/hw/i8259.c
+++ b/hw/i8259.c
@@ -179,9 +179,12 @@  void pic_update_irq(PicState2 *s)
 int64_t irq_time[16];
 #endif
 
-static void i8259_set_irq(void *opaque, int irq, int level)
+static int i8259_set_irq(void *opaque, int irq, int level)
 {
     PicState2 *s = opaque;
+    PicState *pic;
+    int ret = QEMU_IRQ_DELIVERED;
+    int mask;
 
 #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
     if (level != irq_level[irq]) {
@@ -200,8 +203,19 @@  static void i8259_set_irq(void *opaque, int irq, int level)
         irq_time[irq] = qemu_get_clock(vm_clock);
     }
 #endif
-    pic_set_irq1(&s->pics[irq >> 3], irq & 7, level);
+    pic = &s->pics[irq >> 3];
+    irq &= 7;
+    mask = 1 << irq;
+    if (level) {
+        if (pic->imr & mask) {
+            ret = QEMU_IRQ_MASKED;
+        } else if (pic->irr & mask) {
+            ret = QEMU_IRQ_COALESCED;
+        }
+    }
+    pic_set_irq1(pic, irq, level);
     pic_update_irq(s);
+    return ret;
 }
 
 /* acknowledge interrupt 'irq' */
@@ -536,5 +550,5 @@  qemu_irq *i8259_init(qemu_irq parent_irq)
     s->pics[0].pics_state = s;
     s->pics[1].pics_state = s;
     isa_pic = s;
-    return qemu_allocate_irqs(i8259_set_irq, s, 16);
+    return qemu_allocate_feedback_irqs(i8259_set_irq, s, 16);
 }
diff --git a/hw/ioapic.c b/hw/ioapic.c
index 7ad8018..179fe49 100644
--- a/hw/ioapic.c
+++ b/hw/ioapic.c
@@ -51,7 +51,7 @@  struct IOAPICState {
     uint64_t ioredtbl[IOAPIC_NUM_PINS];
 };
 
-static void ioapic_service(IOAPICState *s)
+static int ioapic_service(IOAPICState *s)
 {
     uint8_t i;
     uint8_t trig_mode;
@@ -62,12 +62,16 @@  static void ioapic_service(IOAPICState *s)
     uint8_t dest;
     uint8_t dest_mode;
     uint8_t polarity;
+    int ret = QEMU_IRQ_MASKED;
 
     for (i = 0; i < IOAPIC_NUM_PINS; i++) {
         mask = 1 << i;
         if (s->irr & mask) {
             entry = s->ioredtbl[i];
             if (!(entry & IOAPIC_LVT_MASKED)) {
+                if (ret == QEMU_IRQ_MASKED) {
+                    ret = QEMU_IRQ_COALESCED;
+                }
                 trig_mode = ((entry >> 15) & 1);
                 dest = entry >> 56;
                 dest_mode = (entry >> 11) & 1;
@@ -80,33 +84,39 @@  static void ioapic_service(IOAPICState *s)
                 else
                     vector = entry & 0xff;
 
-                apic_deliver_irq(dest, dest_mode, delivery_mode,
-                                 vector, polarity, trig_mode);
+                if (apic_deliver_irq(dest, dest_mode,
+                                     delivery_mode, vector, polarity,
+                                     trig_mode) == QEMU_IRQ_DELIVERED) {
+                    ret = QEMU_IRQ_DELIVERED;
+                }
             }
         }
     }
+    return ret;
 }
 
-void ioapic_set_irq(void *opaque, int vector, int level)
+static int ioapic_set_irq(void *opaque, int vector, int level)
 {
+    int mapped_vector = vector;
     IOAPICState *s = opaque;
+    int ret = QEMU_IRQ_MASKED;
 
     /* ISA IRQs map to GSI 1-1 except for IRQ0 which maps
      * to GSI 2.  GSI maps to ioapic 1-1.  This is not
      * the cleanest way of doing it but it should work. */
 
     if (vector == 0)
-        vector = 2;
+        mapped_vector = 2;
 
     if (vector >= 0 && vector < IOAPIC_NUM_PINS) {
-        uint32_t mask = 1 << vector;
-        uint64_t entry = s->ioredtbl[vector];
+        uint32_t mask = 1 << mapped_vector;
+        uint64_t entry = s->ioredtbl[mapped_vector];
 
         if ((entry >> 15) & 1) {
             /* level triggered */
             if (level) {
                 s->irr |= mask;
-                ioapic_service(s);
+                ret = ioapic_service(s);
             } else {
                 s->irr &= ~mask;
             }
@@ -114,10 +124,11 @@  void ioapic_set_irq(void *opaque, int vector, int level)
             /* edge triggered */
             if (level) {
                 s->irr |= mask;
-                ioapic_service(s);
+                ret = ioapic_service(s);
             }
         }
     }
+    return ret;
 }
 
 static uint32_t ioapic_mem_readl(void *opaque, target_phys_addr_t addr)
@@ -230,7 +241,6 @@  static CPUWriteMemoryFunc * const ioapic_mem_write[3] = {
 qemu_irq *ioapic_init(void)
 {
     IOAPICState *s;
-    qemu_irq *irq;
     int io_memory;
 
     s = qemu_mallocz(sizeof(IOAPICState));
@@ -242,7 +252,5 @@  qemu_irq *ioapic_init(void)
 
     vmstate_register(0, &vmstate_ioapic, s);
     qemu_register_reset(ioapic_reset, s);
-    irq = qemu_allocate_irqs(ioapic_set_irq, s, IOAPIC_NUM_PINS);
-
-    return irq;
+    return qemu_allocate_feedback_irqs(ioapic_set_irq, s, IOAPIC_NUM_PINS);
 }
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index 571c593..697f723 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -25,7 +25,6 @@ 
 #include "qemu-timer.h"
 #include "sysemu.h"
 #include "pc.h"
-#include "apic.h"
 #include "isa.h"
 #include "hpet_emul.h"
 #include "mc146818rtc.h"
@@ -94,7 +93,7 @@  typedef struct RTCState {
     QEMUTimer *second_timer2;
 } RTCState;
 
-static void rtc_irq_raise(qemu_irq irq)
+static int rtc_irq_raise(qemu_irq irq)
 {
     /* When HPET is operating in legacy mode, RTC interrupts are disabled
      * We block qemu_irq_raise, but not qemu_irq_lower, in case legacy
@@ -102,9 +101,11 @@  static void rtc_irq_raise(qemu_irq irq)
      * be lowered in any case
      */
 #if defined TARGET_I386
-    if (!hpet_in_legacy_mode())
+    if (hpet_in_legacy_mode()) {
+        return QEMU_IRQ_MASKED;
+    }
 #endif
-        qemu_irq_raise(irq);
+    return qemu_irq_raise(irq);
 }
 
 static void rtc_set_time(RTCState *s);
@@ -129,10 +130,8 @@  static void rtc_coalesced_timer(void *opaque)
     RTCState *s = opaque;
 
     if (s->irq_coalesced != 0) {
-        apic_reset_irq_delivered();
         s->cmos_data[RTC_REG_C] |= 0xc0;
-        rtc_irq_raise(s->irq);
-        if (apic_get_irq_delivered()) {
+        if (rtc_irq_raise(s->irq) != QEMU_IRQ_COALESCED) {
             s->irq_coalesced--;
         }
     }
@@ -193,9 +192,7 @@  static void rtc_periodic_timer(void *opaque)
         if(rtc_td_hack) {
             if (s->irq_reinject_on_ack_count >= RTC_REINJECT_ON_ACK_COUNT)
                 s->irq_reinject_on_ack_count = 0;		
-            apic_reset_irq_delivered();
-            rtc_irq_raise(s->irq);
-            if (!apic_get_irq_delivered()) {
+            if (rtc_irq_raise(s->irq) == QEMU_IRQ_COALESCED) {
                 s->irq_coalesced++;
                 rtc_coalesced_timer_update(s);
             }
@@ -475,10 +472,9 @@  static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
             if(s->irq_coalesced &&
                     s->irq_reinject_on_ack_count < RTC_REINJECT_ON_ACK_COUNT) {
                 s->irq_reinject_on_ack_count++;
-                apic_reset_irq_delivered();
-                qemu_irq_raise(s->irq);
-                if (apic_get_irq_delivered())
+                if (qemu_irq_raise(s->irq) != QEMU_IRQ_COALESCED) {
                     s->irq_coalesced--;
+                }
                 break;
             }
 #endif
diff --git a/hw/pc.c b/hw/pc.c
index 631b0ae..ec6c32b 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -67,15 +67,22 @@  struct e820_table {
 
 static struct e820_table e820_table;
 
-void isa_irq_handler(void *opaque, int n, int level)
+int isa_irq_handler(void *opaque, int n, int level)
 {
-    IsaIrqState *isa = (IsaIrqState *)opaque;
+    IsaIrqState *isa = opaque;
+    int ret = QEMU_IRQ_MASKED;
+    int ioapic_ret;
 
     if (n < 16) {
-        qemu_set_irq(isa->i8259[n], level);
+        ret = qemu_set_irq(isa->i8259[n], level);
     }
-    if (isa->ioapic)
-        qemu_set_irq(isa->ioapic[n], level);
+    if (isa->ioapic) {
+        ioapic_ret = qemu_set_irq(isa->ioapic[n], level);
+        if (ioapic_ret == QEMU_IRQ_DELIVERED || ret == QEMU_IRQ_MASKED) {
+            ret = ioapic_ret;
+        }
+    }
+    return ret;
 };
 
 static void ioport80_write(void *opaque, uint32_t addr, uint32_t data)
diff --git a/hw/pc.h b/hw/pc.h
index 73cccef..015412f 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -44,7 +44,7 @@  typedef struct isa_irq_state {
     qemu_irq *ioapic;
 } IsaIrqState;
 
-void isa_irq_handler(void *opaque, int n, int level);
+int isa_irq_handler(void *opaque, int n, int level);
 
 /* i8254.c */
 
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index 70f563a..648b607 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -78,7 +78,7 @@  static void pc_init1(ram_addr_t ram_size,
     if (pci_enabled) {
         isa_irq_state->ioapic = ioapic_init();
     }
-    isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24);
+    isa_irq = qemu_allocate_feedback_irqs(isa_irq_handler, isa_irq_state, 24);
 
     if (pci_enabled) {
         pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq, ram_size);