All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/5] hpet 'driftfix': alleviate time drift with HPET periodic timers
@ 2011-05-09  7:03 ` Ulrich Obergfell
  0 siblings, 0 replies; 16+ messages in thread
From: Ulrich Obergfell @ 2011-05-09  7:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, kvm, jan.kiszka, mtosatti, uobergfe, gcosta, avi

Hi,

This is version 4 of a series of patches that I originally posted in:

    http://lists.gnu.org/archive/html/qemu-devel/2011-03/msg01989.html
    http://lists.gnu.org/archive/html/qemu-devel/2011-03/msg01992.html
    http://lists.gnu.org/archive/html/qemu-devel/2011-03/msg01991.html
    http://lists.gnu.org/archive/html/qemu-devel/2011-03/msg01990.html

    http://article.gmane.org/gmane.comp.emulators.kvm.devel/69325
    http://article.gmane.org/gmane.comp.emulators.kvm.devel/69326
    http://article.gmane.org/gmane.comp.emulators.kvm.devel/69327
    http://article.gmane.org/gmane.comp.emulators.kvm.devel/69328


Changes since version 3:

 in patch part 1/5 and part 4/5

 -  Added stub functions for 'target_reset_irq_delivered' and
    'target_get_irq_delivered'. Added registration functions
    that are used by apic code to replace the stubs.

 -  Removed NULL pointer checks from update_irq().

 in patch part 5/5

 -  A minor modification in hpet_timer_has_tick_backlog().

 -  Renamed the local variable 'irq_count' in hpet_timer() to
    'period_count'.

 -  Driftfix-related fields in struct 'HPETTimer' are no longer
    being initialized/reset in hpet_reset(). Added the function
    hpet_timer_driftfix_reset() which is called when the guest

    .  sets the 'CFG_ENABLE' bit (overall enable)
       in the General Configuration Register.

    .  sets the 'TN_ENABLE' bit (timer N interrupt enable)
       in the Timer N Configuration and Capabilities Register.


Please review and please comment.

Regards,

Uli


Ulrich Obergfell (5):
  hpet 'driftfix': add hooks required to detect coalesced interrupts
    (x86 apic only)
  hpet 'driftfix': add driftfix property to HPETState and DeviceInfo
  hpet 'driftfix': add fields to HPETTimer and VMStateDescription
  hpet 'driftfix': add code in update_irq() to detect coalesced
    interrupts (x86 apic only)
  hpet 'driftfix': add code in hpet_timer() to compensate delayed
    callbacks and coalesced interrupts

 hw/apic.c |    4 ++
 hw/hpet.c |  119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 hw/pc.h   |   13 +++++++
 vl.c      |   13 +++++++
 4 files changed, 145 insertions(+), 4 deletions(-)

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [Qemu-devel] [PATCH v4 0/5] hpet 'driftfix': alleviate time drift with HPET periodic timers
@ 2011-05-09  7:03 ` Ulrich Obergfell
  0 siblings, 0 replies; 16+ messages in thread
From: Ulrich Obergfell @ 2011-05-09  7:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, kvm, jan.kiszka, mtosatti, uobergfe, gcosta, avi

Hi,

This is version 4 of a series of patches that I originally posted in:

    http://lists.gnu.org/archive/html/qemu-devel/2011-03/msg01989.html
    http://lists.gnu.org/archive/html/qemu-devel/2011-03/msg01992.html
    http://lists.gnu.org/archive/html/qemu-devel/2011-03/msg01991.html
    http://lists.gnu.org/archive/html/qemu-devel/2011-03/msg01990.html

    http://article.gmane.org/gmane.comp.emulators.kvm.devel/69325
    http://article.gmane.org/gmane.comp.emulators.kvm.devel/69326
    http://article.gmane.org/gmane.comp.emulators.kvm.devel/69327
    http://article.gmane.org/gmane.comp.emulators.kvm.devel/69328


Changes since version 3:

 in patch part 1/5 and part 4/5

 -  Added stub functions for 'target_reset_irq_delivered' and
    'target_get_irq_delivered'. Added registration functions
    that are used by apic code to replace the stubs.

 -  Removed NULL pointer checks from update_irq().

 in patch part 5/5

 -  A minor modification in hpet_timer_has_tick_backlog().

 -  Renamed the local variable 'irq_count' in hpet_timer() to
    'period_count'.

 -  Driftfix-related fields in struct 'HPETTimer' are no longer
    being initialized/reset in hpet_reset(). Added the function
    hpet_timer_driftfix_reset() which is called when the guest

    .  sets the 'CFG_ENABLE' bit (overall enable)
       in the General Configuration Register.

    .  sets the 'TN_ENABLE' bit (timer N interrupt enable)
       in the Timer N Configuration and Capabilities Register.


Please review and please comment.

Regards,

Uli


Ulrich Obergfell (5):
  hpet 'driftfix': add hooks required to detect coalesced interrupts
    (x86 apic only)
  hpet 'driftfix': add driftfix property to HPETState and DeviceInfo
  hpet 'driftfix': add fields to HPETTimer and VMStateDescription
  hpet 'driftfix': add code in update_irq() to detect coalesced
    interrupts (x86 apic only)
  hpet 'driftfix': add code in hpet_timer() to compensate delayed
    callbacks and coalesced interrupts

 hw/apic.c |    4 ++
 hw/hpet.c |  119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 hw/pc.h   |   13 +++++++
 vl.c      |   13 +++++++
 4 files changed, 145 insertions(+), 4 deletions(-)

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v4 1/5] hpet 'driftfix': add hooks required to detect coalesced interrupts (x86 apic only)
  2011-05-09  7:03 ` [Qemu-devel] " Ulrich Obergfell
@ 2011-05-09  7:03   ` Ulrich Obergfell
  -1 siblings, 0 replies; 16+ messages in thread
From: Ulrich Obergfell @ 2011-05-09  7:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, kvm, jan.kiszka, mtosatti, uobergfe, gcosta, avi

'target_get_irq_delivered' and 'target_reset_irq_delivered' point
to functions that are called by update_irq() to detect coalesced
interrupts. Initially they point to stub functions which pretend
successful interrupt injection. apic code calls two registration
functions to replace the stubs with apic_get_irq_delivered() and
apic_reset_irq_delivered().

This change can be replaced if a generic feedback infrastructure to
track coalesced IRQs for periodic, clock providing devices becomes
available.

Signed-off-by: Ulrich Obergfell <uobergfe@redhat.com>
---
 hw/apic.c |    4 ++++
 hw/pc.h   |   13 +++++++++++++
 vl.c      |   13 +++++++++++++
 3 files changed, 30 insertions(+), 0 deletions(-)

diff --git a/hw/apic.c b/hw/apic.c
index a45b57f..94b1d15 100644
--- a/hw/apic.c
+++ b/hw/apic.c
@@ -17,6 +17,7 @@
  * License along with this library; if not, see <http://www.gnu.org/licenses/>
  */
 #include "hw.h"
+#include "pc.h"
 #include "apic.h"
 #include "ioapic.h"
 #include "qemu-timer.h"
@@ -1143,6 +1144,9 @@ static SysBusDeviceInfo apic_info = {
 
 static void apic_register_devices(void)
 {
+    register_target_get_irq_delivered(apic_get_irq_delivered);
+    register_target_reset_irq_delivered(apic_reset_irq_delivered);
+
     sysbus_register_withprop(&apic_info);
 }
 
diff --git a/hw/pc.h b/hw/pc.h
index 1291e2d..79885f4 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -7,6 +7,19 @@
 #include "fdc.h"
 #include "net.h"
 
+extern int (*target_get_irq_delivered)(void);
+extern void (*target_reset_irq_delivered)(void);
+
+static inline void register_target_get_irq_delivered(int (*func)(void))
+{
+    target_get_irq_delivered = func;
+}
+
+static inline void register_target_reset_irq_delivered(void (*func)(void))
+{
+    target_reset_irq_delivered = func;
+}
+
 /* PC-style peripherals (also used by other machines).  */
 
 /* serial.c */
diff --git a/vl.c b/vl.c
index a143250..a2bbc61 100644
--- a/vl.c
+++ b/vl.c
@@ -233,6 +233,19 @@ const char *prom_envs[MAX_PROM_ENVS];
 const char *nvram = NULL;
 int boot_menu;
 
+static int target_get_irq_delivered_stub(void)
+{
+    return 1;
+}
+
+static void target_reset_irq_delivered_stub(void)
+{
+    return;
+}
+
+int (*target_get_irq_delivered)(void) = target_get_irq_delivered_stub;
+void (*target_reset_irq_delivered)(void) = target_reset_irq_delivered_stub;
+
 typedef struct FWBootEntry FWBootEntry;
 
 struct FWBootEntry {
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [Qemu-devel] [PATCH v4 1/5] hpet 'driftfix': add hooks required to detect coalesced interrupts (x86 apic only)
@ 2011-05-09  7:03   ` Ulrich Obergfell
  0 siblings, 0 replies; 16+ messages in thread
From: Ulrich Obergfell @ 2011-05-09  7:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, kvm, jan.kiszka, mtosatti, uobergfe, gcosta, avi

'target_get_irq_delivered' and 'target_reset_irq_delivered' point
to functions that are called by update_irq() to detect coalesced
interrupts. Initially they point to stub functions which pretend
successful interrupt injection. apic code calls two registration
functions to replace the stubs with apic_get_irq_delivered() and
apic_reset_irq_delivered().

This change can be replaced if a generic feedback infrastructure to
track coalesced IRQs for periodic, clock providing devices becomes
available.

Signed-off-by: Ulrich Obergfell <uobergfe@redhat.com>
---
 hw/apic.c |    4 ++++
 hw/pc.h   |   13 +++++++++++++
 vl.c      |   13 +++++++++++++
 3 files changed, 30 insertions(+), 0 deletions(-)

diff --git a/hw/apic.c b/hw/apic.c
index a45b57f..94b1d15 100644
--- a/hw/apic.c
+++ b/hw/apic.c
@@ -17,6 +17,7 @@
  * License along with this library; if not, see <http://www.gnu.org/licenses/>
  */
 #include "hw.h"
+#include "pc.h"
 #include "apic.h"
 #include "ioapic.h"
 #include "qemu-timer.h"
@@ -1143,6 +1144,9 @@ static SysBusDeviceInfo apic_info = {
 
 static void apic_register_devices(void)
 {
+    register_target_get_irq_delivered(apic_get_irq_delivered);
+    register_target_reset_irq_delivered(apic_reset_irq_delivered);
+
     sysbus_register_withprop(&apic_info);
 }
 
diff --git a/hw/pc.h b/hw/pc.h
index 1291e2d..79885f4 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -7,6 +7,19 @@
 #include "fdc.h"
 #include "net.h"
 
+extern int (*target_get_irq_delivered)(void);
+extern void (*target_reset_irq_delivered)(void);
+
+static inline void register_target_get_irq_delivered(int (*func)(void))
+{
+    target_get_irq_delivered = func;
+}
+
+static inline void register_target_reset_irq_delivered(void (*func)(void))
+{
+    target_reset_irq_delivered = func;
+}
+
 /* PC-style peripherals (also used by other machines).  */
 
 /* serial.c */
diff --git a/vl.c b/vl.c
index a143250..a2bbc61 100644
--- a/vl.c
+++ b/vl.c
@@ -233,6 +233,19 @@ const char *prom_envs[MAX_PROM_ENVS];
 const char *nvram = NULL;
 int boot_menu;
 
+static int target_get_irq_delivered_stub(void)
+{
+    return 1;
+}
+
+static void target_reset_irq_delivered_stub(void)
+{
+    return;
+}
+
+int (*target_get_irq_delivered)(void) = target_get_irq_delivered_stub;
+void (*target_reset_irq_delivered)(void) = target_reset_irq_delivered_stub;
+
 typedef struct FWBootEntry FWBootEntry;
 
 struct FWBootEntry {
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 2/5] hpet 'driftfix': add driftfix property to HPETState and DeviceInfo
  2011-05-09  7:03 ` [Qemu-devel] " Ulrich Obergfell
@ 2011-05-09  7:03   ` Ulrich Obergfell
  -1 siblings, 0 replies; 16+ messages in thread
From: Ulrich Obergfell @ 2011-05-09  7:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: kvm, uobergfe, gcosta, avi, mtosatti, aliguori, jan.kiszka

driftfix is a 'bit type' property. Compensation of delayed callbacks
and coalesced interrupts can be enabled with the command line option

    -global hpet.driftfix=on

driftfix is 'off' (disabled) by default.

Signed-off-by: Ulrich Obergfell <uobergfe@redhat.com>
---
 hw/hpet.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/hw/hpet.c b/hw/hpet.c
index 6ce07bc..7513065 100644
--- a/hw/hpet.c
+++ b/hw/hpet.c
@@ -72,6 +72,8 @@ typedef struct HPETState {
     uint64_t isr;               /* interrupt status reg */
     uint64_t hpet_counter;      /* main counter */
     uint8_t  hpet_id;           /* instance id */
+
+    uint32_t driftfix;
 } HPETState;
 
 static uint32_t hpet_in_legacy_mode(HPETState *s)
@@ -738,6 +740,7 @@ static SysBusDeviceInfo hpet_device_info = {
     .qdev.props = (Property[]) {
         DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS),
         DEFINE_PROP_BIT("msi", HPETState, flags, HPET_MSI_SUPPORT, false),
+        DEFINE_PROP_BIT("driftfix", HPETState, driftfix, 0, false),
         DEFINE_PROP_END_OF_LIST(),
     },
 };
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [Qemu-devel] [PATCH v4 2/5] hpet 'driftfix': add driftfix property to HPETState and DeviceInfo
@ 2011-05-09  7:03   ` Ulrich Obergfell
  0 siblings, 0 replies; 16+ messages in thread
From: Ulrich Obergfell @ 2011-05-09  7:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, kvm, jan.kiszka, mtosatti, uobergfe, gcosta, avi

driftfix is a 'bit type' property. Compensation of delayed callbacks
and coalesced interrupts can be enabled with the command line option

    -global hpet.driftfix=on

driftfix is 'off' (disabled) by default.

Signed-off-by: Ulrich Obergfell <uobergfe@redhat.com>
---
 hw/hpet.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/hw/hpet.c b/hw/hpet.c
index 6ce07bc..7513065 100644
--- a/hw/hpet.c
+++ b/hw/hpet.c
@@ -72,6 +72,8 @@ typedef struct HPETState {
     uint64_t isr;               /* interrupt status reg */
     uint64_t hpet_counter;      /* main counter */
     uint8_t  hpet_id;           /* instance id */
+
+    uint32_t driftfix;
 } HPETState;
 
 static uint32_t hpet_in_legacy_mode(HPETState *s)
@@ -738,6 +740,7 @@ static SysBusDeviceInfo hpet_device_info = {
     .qdev.props = (Property[]) {
         DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS),
         DEFINE_PROP_BIT("msi", HPETState, flags, HPET_MSI_SUPPORT, false),
+        DEFINE_PROP_BIT("driftfix", HPETState, driftfix, 0, false),
         DEFINE_PROP_END_OF_LIST(),
     },
 };
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 3/5] hpet 'driftfix': add fields to HPETTimer and VMStateDescription
  2011-05-09  7:03 ` [Qemu-devel] " Ulrich Obergfell
@ 2011-05-09  7:03   ` Ulrich Obergfell
  -1 siblings, 0 replies; 16+ messages in thread
From: Ulrich Obergfell @ 2011-05-09  7:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: kvm, uobergfe, gcosta, avi, mtosatti, aliguori, jan.kiszka

The new fields in HPETTimer are covered by a separate VMStateDescription
which is a subsection of 'vmstate_hpet_timer'. They are only migrated if

    -global hpet.driftfix=on

Signed-off-by: Ulrich Obergfell <uobergfe@redhat.com>
---
 hw/hpet.c |   33 +++++++++++++++++++++++++++++++++
 1 files changed, 33 insertions(+), 0 deletions(-)

diff --git a/hw/hpet.c b/hw/hpet.c
index 7513065..7ab6e62 100644
--- a/hw/hpet.c
+++ b/hw/hpet.c
@@ -55,6 +55,10 @@ typedef struct HPETTimer {  /* timers */
     uint8_t wrap_flag;      /* timer pop will indicate wrap for one-shot 32-bit
                              * mode. Next pop will be actual timer expiration.
                              */
+    uint64_t prev_period;
+    uint64_t ticks_not_accounted;
+    uint32_t irq_rate;
+    uint32_t divisor;
 } HPETTimer;
 
 typedef struct HPETState {
@@ -246,6 +250,27 @@ static int hpet_post_load(void *opaque, int version_id)
     return 0;
 }
 
+static bool hpet_timer_driftfix_vmstate_needed(void *opaque)
+{
+    HPETTimer *t = opaque;
+
+    return (t->state->driftfix != 0);
+}
+
+static const VMStateDescription vmstate_hpet_timer_driftfix = {
+    .name = "hpet_timer_driftfix",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT64(prev_period, HPETTimer),
+        VMSTATE_UINT64(ticks_not_accounted, HPETTimer),
+        VMSTATE_UINT32(irq_rate, HPETTimer),
+        VMSTATE_UINT32(divisor, HPETTimer),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static const VMStateDescription vmstate_hpet_timer = {
     .name = "hpet_timer",
     .version_id = 1,
@@ -260,6 +285,14 @@ static const VMStateDescription vmstate_hpet_timer = {
         VMSTATE_UINT8(wrap_flag, HPETTimer),
         VMSTATE_TIMER(qemu_timer, HPETTimer),
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (VMStateSubsection []) {
+        {
+            .vmsd = &vmstate_hpet_timer_driftfix,
+            .needed = hpet_timer_driftfix_vmstate_needed,
+        }, {
+            /* empty */
+        }
     }
 };
 
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [Qemu-devel] [PATCH v4 3/5] hpet 'driftfix': add fields to HPETTimer and VMStateDescription
@ 2011-05-09  7:03   ` Ulrich Obergfell
  0 siblings, 0 replies; 16+ messages in thread
From: Ulrich Obergfell @ 2011-05-09  7:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, kvm, jan.kiszka, mtosatti, uobergfe, gcosta, avi

The new fields in HPETTimer are covered by a separate VMStateDescription
which is a subsection of 'vmstate_hpet_timer'. They are only migrated if

    -global hpet.driftfix=on

Signed-off-by: Ulrich Obergfell <uobergfe@redhat.com>
---
 hw/hpet.c |   33 +++++++++++++++++++++++++++++++++
 1 files changed, 33 insertions(+), 0 deletions(-)

diff --git a/hw/hpet.c b/hw/hpet.c
index 7513065..7ab6e62 100644
--- a/hw/hpet.c
+++ b/hw/hpet.c
@@ -55,6 +55,10 @@ typedef struct HPETTimer {  /* timers */
     uint8_t wrap_flag;      /* timer pop will indicate wrap for one-shot 32-bit
                              * mode. Next pop will be actual timer expiration.
                              */
+    uint64_t prev_period;
+    uint64_t ticks_not_accounted;
+    uint32_t irq_rate;
+    uint32_t divisor;
 } HPETTimer;
 
 typedef struct HPETState {
@@ -246,6 +250,27 @@ static int hpet_post_load(void *opaque, int version_id)
     return 0;
 }
 
+static bool hpet_timer_driftfix_vmstate_needed(void *opaque)
+{
+    HPETTimer *t = opaque;
+
+    return (t->state->driftfix != 0);
+}
+
+static const VMStateDescription vmstate_hpet_timer_driftfix = {
+    .name = "hpet_timer_driftfix",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT64(prev_period, HPETTimer),
+        VMSTATE_UINT64(ticks_not_accounted, HPETTimer),
+        VMSTATE_UINT32(irq_rate, HPETTimer),
+        VMSTATE_UINT32(divisor, HPETTimer),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static const VMStateDescription vmstate_hpet_timer = {
     .name = "hpet_timer",
     .version_id = 1,
@@ -260,6 +285,14 @@ static const VMStateDescription vmstate_hpet_timer = {
         VMSTATE_UINT8(wrap_flag, HPETTimer),
         VMSTATE_TIMER(qemu_timer, HPETTimer),
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (VMStateSubsection []) {
+        {
+            .vmsd = &vmstate_hpet_timer_driftfix,
+            .needed = hpet_timer_driftfix_vmstate_needed,
+        }, {
+            /* empty */
+        }
     }
 };
 
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 4/5] hpet 'driftfix': add code in update_irq() to detect coalesced interrupts (x86 apic only)
  2011-05-09  7:03 ` [Qemu-devel] " Ulrich Obergfell
@ 2011-05-09  7:03   ` Ulrich Obergfell
  -1 siblings, 0 replies; 16+ messages in thread
From: Ulrich Obergfell @ 2011-05-09  7:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: kvm, uobergfe, gcosta, avi, mtosatti, aliguori, jan.kiszka

update_irq() uses a similar method as in 'rtc_td_hack' to detect
coalesced interrupts. The function entry addresses are retrieved
from 'target_get_irq_delivered' and 'target_reset_irq_delivered'.

This change can be replaced if a generic feedback infrastructure to
track coalesced IRQs for periodic, clock providing devices becomes
available.

Signed-off-by: Ulrich Obergfell <uobergfe@redhat.com>
---
 hw/hpet.c |   13 +++++++++++--
 1 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/hw/hpet.c b/hw/hpet.c
index 7ab6e62..e57c654 100644
--- a/hw/hpet.c
+++ b/hw/hpet.c
@@ -175,11 +175,12 @@ static inline uint64_t hpet_calculate_diff(HPETTimer *t, uint64_t current)
     }
 }
 
-static void update_irq(struct HPETTimer *timer, int set)
+static int update_irq(struct HPETTimer *timer, int set)
 {
     uint64_t mask;
     HPETState *s;
     int route;
+    int irq_delivered = 1;
 
     if (timer->tn <= 1 && hpet_in_legacy_mode(timer->state)) {
         /* if LegacyReplacementRoute bit is set, HPET specification requires
@@ -204,8 +205,16 @@ static void update_irq(struct HPETTimer *timer, int set)
         qemu_irq_raise(s->irqs[route]);
     } else {
         s->isr &= ~mask;
-        qemu_irq_pulse(s->irqs[route]);
+        if (s->driftfix) {
+            target_reset_irq_delivered();
+            qemu_irq_raise(s->irqs[route]);
+            irq_delivered = target_get_irq_delivered();
+            qemu_irq_lower(s->irqs[route]);
+        } else {
+            qemu_irq_pulse(s->irqs[route]);
+        }
     }
+    return irq_delivered;
 }
 
 static void hpet_pre_save(void *opaque)
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [Qemu-devel] [PATCH v4 4/5] hpet 'driftfix': add code in update_irq() to detect coalesced interrupts (x86 apic only)
@ 2011-05-09  7:03   ` Ulrich Obergfell
  0 siblings, 0 replies; 16+ messages in thread
From: Ulrich Obergfell @ 2011-05-09  7:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, kvm, jan.kiszka, mtosatti, uobergfe, gcosta, avi

update_irq() uses a similar method as in 'rtc_td_hack' to detect
coalesced interrupts. The function entry addresses are retrieved
from 'target_get_irq_delivered' and 'target_reset_irq_delivered'.

This change can be replaced if a generic feedback infrastructure to
track coalesced IRQs for periodic, clock providing devices becomes
available.

Signed-off-by: Ulrich Obergfell <uobergfe@redhat.com>
---
 hw/hpet.c |   13 +++++++++++--
 1 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/hw/hpet.c b/hw/hpet.c
index 7ab6e62..e57c654 100644
--- a/hw/hpet.c
+++ b/hw/hpet.c
@@ -175,11 +175,12 @@ static inline uint64_t hpet_calculate_diff(HPETTimer *t, uint64_t current)
     }
 }
 
-static void update_irq(struct HPETTimer *timer, int set)
+static int update_irq(struct HPETTimer *timer, int set)
 {
     uint64_t mask;
     HPETState *s;
     int route;
+    int irq_delivered = 1;
 
     if (timer->tn <= 1 && hpet_in_legacy_mode(timer->state)) {
         /* if LegacyReplacementRoute bit is set, HPET specification requires
@@ -204,8 +205,16 @@ static void update_irq(struct HPETTimer *timer, int set)
         qemu_irq_raise(s->irqs[route]);
     } else {
         s->isr &= ~mask;
-        qemu_irq_pulse(s->irqs[route]);
+        if (s->driftfix) {
+            target_reset_irq_delivered();
+            qemu_irq_raise(s->irqs[route]);
+            irq_delivered = target_get_irq_delivered();
+            qemu_irq_lower(s->irqs[route]);
+        } else {
+            qemu_irq_pulse(s->irqs[route]);
+        }
     }
+    return irq_delivered;
 }
 
 static void hpet_pre_save(void *opaque)
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 5/5] hpet 'driftfix': add code in hpet_timer() to compensate delayed callbacks and coalesced interrupts
  2011-05-09  7:03 ` [Qemu-devel] " Ulrich Obergfell
@ 2011-05-09  7:03   ` Ulrich Obergfell
  -1 siblings, 0 replies; 16+ messages in thread
From: Ulrich Obergfell @ 2011-05-09  7:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: kvm, uobergfe, gcosta, avi, mtosatti, aliguori, jan.kiszka

Loss of periodic timer interrupts caused by delayed callbacks and by
interrupt coalescing is compensated by gradually injecting additional
interrupts during subsequent timer intervals, starting at a rate of
one additional interrupt per interval. The injection of additional
interrupts is based on a backlog of unaccounted HPET clock periods
(new HPETTimer field 'ticks_not_accounted'). The backlog increases
due to delayed callbacks and coalesced interrupts, and it decreases
if an interrupt was injected successfully. If the backlog increases
while compensation is still in progress, the rate at which additional
interrupts are injected is increased too. A limit is imposed on the
backlog and on the rate.

Injecting additional timer interrupts to compensate lost interrupts
can alleviate long term time drift. However, on a short time scale,
this method can have the side effect of making virtual machine time
intermittently pass slower and faster than real time (depending on
the guest's time keeping algorithm). Compensation is disabled by
default and can be enabled for guests where this behaviour may be
acceptable.

Signed-off-by: Ulrich Obergfell <uobergfe@redhat.com>
---
 hw/hpet.c |   70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 68 insertions(+), 2 deletions(-)

diff --git a/hw/hpet.c b/hw/hpet.c
index e57c654..519fc6b 100644
--- a/hw/hpet.c
+++ b/hw/hpet.c
@@ -31,6 +31,7 @@
 #include "hpet_emul.h"
 #include "sysbus.h"
 #include "mc146818rtc.h"
+#include <assert.h>
 
 //#define HPET_DEBUG
 #ifdef HPET_DEBUG
@@ -41,6 +42,9 @@
 
 #define HPET_MSI_SUPPORT        0
 
+#define MAX_TICKS_NOT_ACCOUNTED     (uint64_t)500000000 /* 5 sec */
+#define MAX_IRQ_RATE                (uint32_t)10
+
 struct HPETState;
 typedef struct HPETTimer {  /* timers */
     uint8_t tn;             /*timer number*/
@@ -324,14 +328,35 @@ static const VMStateDescription vmstate_hpet = {
     }
 };
 
+static void hpet_timer_driftfix_reset(HPETTimer *t)
+{
+    if (t->state->driftfix && timer_is_periodic(t)) {
+        t->ticks_not_accounted = t->prev_period = t->period;
+        t->irq_rate = 1;
+        t->divisor = 1;
+    }
+}
+
+static bool hpet_timer_has_tick_backlog(HPETTimer *t)
+{
+    uint64_t backlog = 0;
+
+    if (t->ticks_not_accounted >= t->period + t->prev_period) {
+        backlog = t->ticks_not_accounted - (t->period + t->prev_period);
+    }
+    return (backlog >= t->period);
+}
+
 /*
  * timer expiration callback
  */
 static void hpet_timer(void *opaque)
 {
     HPETTimer *t = opaque;
+    HPETState *s = t->state;
     uint64_t diff;
-
+    int irq_delivered = 0;
+    uint32_t period_count = 0;
     uint64_t period = t->period;
     uint64_t cur_tick = hpet_get_ticks(t->state);
 
@@ -339,13 +364,37 @@ static void hpet_timer(void *opaque)
         if (t->config & HPET_TN_32BIT) {
             while (hpet_time_after(cur_tick, t->cmp)) {
                 t->cmp = (uint32_t)(t->cmp + t->period);
+                t->ticks_not_accounted += t->period;
+                period_count++;
             }
         } else {
             while (hpet_time_after64(cur_tick, t->cmp)) {
                 t->cmp += period;
+                t->ticks_not_accounted += period;
+                period_count++;
             }
         }
         diff = hpet_calculate_diff(t, cur_tick);
+        if (s->driftfix) {
+            if (t->ticks_not_accounted > MAX_TICKS_NOT_ACCOUNTED) {
+                t->ticks_not_accounted = t->period + t->prev_period;
+            }
+            if (hpet_timer_has_tick_backlog(t)) {
+                if (t->irq_rate == 1 || period_count > 1) {
+                    t->irq_rate++;
+                    t->irq_rate = MIN(t->irq_rate, MAX_IRQ_RATE);
+                }
+                if (t->divisor == 0) {
+                    assert(period_count);
+                }
+                if (period_count) {
+                    t->divisor = t->irq_rate;
+                }
+                diff /= t->divisor--;
+            } else {
+                t->irq_rate = 1;
+            }
+        }
         qemu_mod_timer(t->qemu_timer,
                        qemu_get_clock_ns(vm_clock) + (int64_t)ticks_to_ns(diff));
     } else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) {
@@ -356,7 +405,22 @@ static void hpet_timer(void *opaque)
             t->wrap_flag = 0;
         }
     }
-    update_irq(t, 1);
+    if (s->driftfix && timer_is_periodic(t) && period != 0) {
+        if (t->ticks_not_accounted >= t->period + t->prev_period) {
+            irq_delivered = update_irq(t, 1);
+            if (irq_delivered) {
+                t->ticks_not_accounted -= t->prev_period;
+                t->prev_period = t->period;
+            } else {
+                if (period_count) {
+                    t->irq_rate++;
+                    t->irq_rate = MIN(t->irq_rate, MAX_IRQ_RATE);
+                }
+            }
+        }
+    } else {
+        update_irq(t, 1);
+    }
 }
 
 static void hpet_set_timer(HPETTimer *t)
@@ -525,6 +589,7 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr,
                 timer->period = (uint32_t)timer->period;
             }
             if (activating_bit(old_val, new_val, HPET_TN_ENABLE)) {
+                hpet_timer_driftfix_reset(timer);
                 hpet_set_timer(timer);
             } else if (deactivating_bit(old_val, new_val, HPET_TN_ENABLE)) {
                 hpet_del_timer(timer);
@@ -599,6 +664,7 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr,
                     ticks_to_ns(s->hpet_counter) - qemu_get_clock_ns(vm_clock);
                 for (i = 0; i < s->num_timers; i++) {
                     if ((&s->timer[i])->cmp != ~0ULL) {
+                        hpet_timer_driftfix_reset(&s->timer[i]);
                         hpet_set_timer(&s->timer[i]);
                     }
                 }
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [Qemu-devel] [PATCH v4 5/5] hpet 'driftfix': add code in hpet_timer() to compensate delayed callbacks and coalesced interrupts
@ 2011-05-09  7:03   ` Ulrich Obergfell
  0 siblings, 0 replies; 16+ messages in thread
From: Ulrich Obergfell @ 2011-05-09  7:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, kvm, jan.kiszka, mtosatti, uobergfe, gcosta, avi

Loss of periodic timer interrupts caused by delayed callbacks and by
interrupt coalescing is compensated by gradually injecting additional
interrupts during subsequent timer intervals, starting at a rate of
one additional interrupt per interval. The injection of additional
interrupts is based on a backlog of unaccounted HPET clock periods
(new HPETTimer field 'ticks_not_accounted'). The backlog increases
due to delayed callbacks and coalesced interrupts, and it decreases
if an interrupt was injected successfully. If the backlog increases
while compensation is still in progress, the rate at which additional
interrupts are injected is increased too. A limit is imposed on the
backlog and on the rate.

Injecting additional timer interrupts to compensate lost interrupts
can alleviate long term time drift. However, on a short time scale,
this method can have the side effect of making virtual machine time
intermittently pass slower and faster than real time (depending on
the guest's time keeping algorithm). Compensation is disabled by
default and can be enabled for guests where this behaviour may be
acceptable.

Signed-off-by: Ulrich Obergfell <uobergfe@redhat.com>
---
 hw/hpet.c |   70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 68 insertions(+), 2 deletions(-)

diff --git a/hw/hpet.c b/hw/hpet.c
index e57c654..519fc6b 100644
--- a/hw/hpet.c
+++ b/hw/hpet.c
@@ -31,6 +31,7 @@
 #include "hpet_emul.h"
 #include "sysbus.h"
 #include "mc146818rtc.h"
+#include <assert.h>
 
 //#define HPET_DEBUG
 #ifdef HPET_DEBUG
@@ -41,6 +42,9 @@
 
 #define HPET_MSI_SUPPORT        0
 
+#define MAX_TICKS_NOT_ACCOUNTED     (uint64_t)500000000 /* 5 sec */
+#define MAX_IRQ_RATE                (uint32_t)10
+
 struct HPETState;
 typedef struct HPETTimer {  /* timers */
     uint8_t tn;             /*timer number*/
@@ -324,14 +328,35 @@ static const VMStateDescription vmstate_hpet = {
     }
 };
 
+static void hpet_timer_driftfix_reset(HPETTimer *t)
+{
+    if (t->state->driftfix && timer_is_periodic(t)) {
+        t->ticks_not_accounted = t->prev_period = t->period;
+        t->irq_rate = 1;
+        t->divisor = 1;
+    }
+}
+
+static bool hpet_timer_has_tick_backlog(HPETTimer *t)
+{
+    uint64_t backlog = 0;
+
+    if (t->ticks_not_accounted >= t->period + t->prev_period) {
+        backlog = t->ticks_not_accounted - (t->period + t->prev_period);
+    }
+    return (backlog >= t->period);
+}
+
 /*
  * timer expiration callback
  */
 static void hpet_timer(void *opaque)
 {
     HPETTimer *t = opaque;
+    HPETState *s = t->state;
     uint64_t diff;
-
+    int irq_delivered = 0;
+    uint32_t period_count = 0;
     uint64_t period = t->period;
     uint64_t cur_tick = hpet_get_ticks(t->state);
 
@@ -339,13 +364,37 @@ static void hpet_timer(void *opaque)
         if (t->config & HPET_TN_32BIT) {
             while (hpet_time_after(cur_tick, t->cmp)) {
                 t->cmp = (uint32_t)(t->cmp + t->period);
+                t->ticks_not_accounted += t->period;
+                period_count++;
             }
         } else {
             while (hpet_time_after64(cur_tick, t->cmp)) {
                 t->cmp += period;
+                t->ticks_not_accounted += period;
+                period_count++;
             }
         }
         diff = hpet_calculate_diff(t, cur_tick);
+        if (s->driftfix) {
+            if (t->ticks_not_accounted > MAX_TICKS_NOT_ACCOUNTED) {
+                t->ticks_not_accounted = t->period + t->prev_period;
+            }
+            if (hpet_timer_has_tick_backlog(t)) {
+                if (t->irq_rate == 1 || period_count > 1) {
+                    t->irq_rate++;
+                    t->irq_rate = MIN(t->irq_rate, MAX_IRQ_RATE);
+                }
+                if (t->divisor == 0) {
+                    assert(period_count);
+                }
+                if (period_count) {
+                    t->divisor = t->irq_rate;
+                }
+                diff /= t->divisor--;
+            } else {
+                t->irq_rate = 1;
+            }
+        }
         qemu_mod_timer(t->qemu_timer,
                        qemu_get_clock_ns(vm_clock) + (int64_t)ticks_to_ns(diff));
     } else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) {
@@ -356,7 +405,22 @@ static void hpet_timer(void *opaque)
             t->wrap_flag = 0;
         }
     }
-    update_irq(t, 1);
+    if (s->driftfix && timer_is_periodic(t) && period != 0) {
+        if (t->ticks_not_accounted >= t->period + t->prev_period) {
+            irq_delivered = update_irq(t, 1);
+            if (irq_delivered) {
+                t->ticks_not_accounted -= t->prev_period;
+                t->prev_period = t->period;
+            } else {
+                if (period_count) {
+                    t->irq_rate++;
+                    t->irq_rate = MIN(t->irq_rate, MAX_IRQ_RATE);
+                }
+            }
+        }
+    } else {
+        update_irq(t, 1);
+    }
 }
 
 static void hpet_set_timer(HPETTimer *t)
@@ -525,6 +589,7 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr,
                 timer->period = (uint32_t)timer->period;
             }
             if (activating_bit(old_val, new_val, HPET_TN_ENABLE)) {
+                hpet_timer_driftfix_reset(timer);
                 hpet_set_timer(timer);
             } else if (deactivating_bit(old_val, new_val, HPET_TN_ENABLE)) {
                 hpet_del_timer(timer);
@@ -599,6 +664,7 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr,
                     ticks_to_ns(s->hpet_counter) - qemu_get_clock_ns(vm_clock);
                 for (i = 0; i < s->num_timers; i++) {
                     if ((&s->timer[i])->cmp != ~0ULL) {
+                        hpet_timer_driftfix_reset(&s->timer[i]);
                         hpet_set_timer(&s->timer[i]);
                     }
                 }
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH v4 5/5] hpet 'driftfix': add code in hpet_timer() to compensate delayed callbacks and coalesced interrupts
  2011-05-09  7:03   ` [Qemu-devel] " Ulrich Obergfell
@ 2011-05-12  1:58     ` Zachary Amsden
  -1 siblings, 0 replies; 16+ messages in thread
From: Zachary Amsden @ 2011-05-12  1:58 UTC (permalink / raw)
  To: Ulrich Obergfell
  Cc: qemu-devel, aliguori, kvm, jan.kiszka, mtosatti, gcosta, avi

On 05/09/2011 12:03 AM, Ulrich Obergfell wrote:
> Loss of periodic timer interrupts caused by delayed callbacks and by
> interrupt coalescing is compensated by gradually injecting additional
> interrupts during subsequent timer intervals, starting at a rate of
> one additional interrupt per interval. The injection of additional
> interrupts is based on a backlog of unaccounted HPET clock periods
> (new HPETTimer field 'ticks_not_accounted'). The backlog increases
> due to delayed callbacks and coalesced interrupts, and it decreases
> if an interrupt was injected successfully. If the backlog increases
> while compensation is still in progress, the rate at which additional
> interrupts are injected is increased too. A limit is imposed on the
> backlog and on the rate.
>
> Injecting additional timer interrupts to compensate lost interrupts
> can alleviate long term time drift. However, on a short time scale,
> this method can have the side effect of making virtual machine time
> intermittently pass slower and faster than real time (depending on
> the guest's time keeping algorithm). Compensation is disabled by
> default and can be enabled for guests where this behaviour may be
> acceptable.
>
> Signed-off-by: Ulrich Obergfell<uobergfe@redhat.com>
> ---
>   hw/hpet.c |   70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>   1 files changed, 68 insertions(+), 2 deletions(-)
>
> diff --git a/hw/hpet.c b/hw/hpet.c
> index e57c654..519fc6b 100644
> --- a/hw/hpet.c
> +++ b/hw/hpet.c
> @@ -31,6 +31,7 @@
>   #include "hpet_emul.h"
>   #include "sysbus.h"
>   #include "mc146818rtc.h"
> +#include<assert.h>
>
>   //#define HPET_DEBUG
>   #ifdef HPET_DEBUG
> @@ -41,6 +42,9 @@
>
>   #define HPET_MSI_SUPPORT        0
>
> +#define MAX_TICKS_NOT_ACCOUNTED     (uint64_t)500000000 /* 5 sec */
> +#define MAX_IRQ_RATE                (uint32_t)10
> +
>   struct HPETState;
>   typedef struct HPETTimer {  /* timers */
>       uint8_t tn;             /*timer number*/
> @@ -324,14 +328,35 @@ static const VMStateDescription vmstate_hpet = {
>       }
>   };
>
> +static void hpet_timer_driftfix_reset(HPETTimer *t)
> +{
> +    if (t->state->driftfix&&  timer_is_periodic(t)) {
> +        t->ticks_not_accounted = t->prev_period = t->period;
>    

This is rather confusing.  Clearly, ticks_not_accounted isn't actually 
ticks not accounted, it's a different variable entirely which is based 
on the current period.

If it were actually ticks_not_accounted, it should be reset to zero.

> +        t->irq_rate = 1;
> +        t->divisor = 1;
> +    }
> +}
> +
> +static bool hpet_timer_has_tick_backlog(HPETTimer *t)
> +{
> +    uint64_t backlog = 0;
> +
> +    if (t->ticks_not_accounted>= t->period + t->prev_period) {
> +        backlog = t->ticks_not_accounted - (t->period + t->prev_period);
> +    }
> +    return (backlog>= t->period);
> +}
> +
>   /*
>    * timer expiration callback
>    */
>   static void hpet_timer(void *opaque)
>   {
>       HPETTimer *t = opaque;
> +    HPETState *s = t->state;
>       uint64_t diff;
> -
> +    int irq_delivered = 0;
> +    uint32_t period_count = 0;
>       uint64_t period = t->period;
>       uint64_t cur_tick = hpet_get_ticks(t->state);
>
> @@ -339,13 +364,37 @@ static void hpet_timer(void *opaque)
>           if (t->config&  HPET_TN_32BIT) {
>               while (hpet_time_after(cur_tick, t->cmp)) {
>                   t->cmp = (uint32_t)(t->cmp + t->period);
> +                t->ticks_not_accounted += t->period;
> +                period_count++;
>               }
>           } else {
>               while (hpet_time_after64(cur_tick, t->cmp)) {
>                   t->cmp += period;
> +                t->ticks_not_accounted += period;
> +                period_count++;
>               }
>           }
>           diff = hpet_calculate_diff(t, cur_tick);
> +        if (s->driftfix) {
> +            if (t->ticks_not_accounted>  MAX_TICKS_NOT_ACCOUNTED) {
> +                t->ticks_not_accounted = t->period + t->prev_period;
> +            }
> +            if (hpet_timer_has_tick_backlog(t)) {
> +                if (t->irq_rate == 1 || period_count>  1) {
> +                    t->irq_rate++;
> +                    t->irq_rate = MIN(t->irq_rate, MAX_IRQ_RATE);
> +                }
> +                if (t->divisor == 0) {
> +                    assert(period_count);
> +                }
> +                if (period_count) {
> +                    t->divisor = t->irq_rate;
> +                }
> +                diff /= t->divisor--;
>    

Why subtracting from the divisor?  Shouldn't the divisor and irq_rate 
change in lockstep?

> +            } else {
> +                t->irq_rate = 1;
> +            }
> +        }
>           qemu_mod_timer(t->qemu_timer,
>                          qemu_get_clock_ns(vm_clock) + (int64_t)ticks_to_ns(diff));
>       } else if (t->config&  HPET_TN_32BIT&&  !timer_is_periodic(t)) {
> @@ -356,7 +405,22 @@ static void hpet_timer(void *opaque)
>               t->wrap_flag = 0;
>           }
>       }
> -    update_irq(t, 1);
> +    if (s->driftfix&&  timer_is_periodic(t)&&  period != 0) {
> +        if (t->ticks_not_accounted>= t->period + t->prev_period) {
> +            irq_delivered = update_irq(t, 1);
> +            if (irq_delivered) {
> +                t->ticks_not_accounted -= t->prev_period;
> +                t->prev_period = t->period;
> +            } else {
> +                if (period_count) {
> +                    t->irq_rate++;
> +                    t->irq_rate = MIN(t->irq_rate, MAX_IRQ_RATE);
> +                }
> +            }
> +        }
> +    } else {
> +        update_irq(t, 1);
> +    }
>   }
>    

Overall I think the intention is correct, and the code may be as well, 
but anything with this amount of subtle manipulations and math tricks 
needs to be more well commented for others to understand it.

I'll try to parse it again later, hopefully with better luck,

Zach

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH v4 5/5] hpet 'driftfix': add code in hpet_timer() to compensate delayed callbacks and coalesced interrupts
@ 2011-05-12  1:58     ` Zachary Amsden
  0 siblings, 0 replies; 16+ messages in thread
From: Zachary Amsden @ 2011-05-12  1:58 UTC (permalink / raw)
  To: Ulrich Obergfell
  Cc: aliguori, kvm, jan.kiszka, mtosatti, qemu-devel, gcosta, avi

On 05/09/2011 12:03 AM, Ulrich Obergfell wrote:
> Loss of periodic timer interrupts caused by delayed callbacks and by
> interrupt coalescing is compensated by gradually injecting additional
> interrupts during subsequent timer intervals, starting at a rate of
> one additional interrupt per interval. The injection of additional
> interrupts is based on a backlog of unaccounted HPET clock periods
> (new HPETTimer field 'ticks_not_accounted'). The backlog increases
> due to delayed callbacks and coalesced interrupts, and it decreases
> if an interrupt was injected successfully. If the backlog increases
> while compensation is still in progress, the rate at which additional
> interrupts are injected is increased too. A limit is imposed on the
> backlog and on the rate.
>
> Injecting additional timer interrupts to compensate lost interrupts
> can alleviate long term time drift. However, on a short time scale,
> this method can have the side effect of making virtual machine time
> intermittently pass slower and faster than real time (depending on
> the guest's time keeping algorithm). Compensation is disabled by
> default and can be enabled for guests where this behaviour may be
> acceptable.
>
> Signed-off-by: Ulrich Obergfell<uobergfe@redhat.com>
> ---
>   hw/hpet.c |   70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>   1 files changed, 68 insertions(+), 2 deletions(-)
>
> diff --git a/hw/hpet.c b/hw/hpet.c
> index e57c654..519fc6b 100644
> --- a/hw/hpet.c
> +++ b/hw/hpet.c
> @@ -31,6 +31,7 @@
>   #include "hpet_emul.h"
>   #include "sysbus.h"
>   #include "mc146818rtc.h"
> +#include<assert.h>
>
>   //#define HPET_DEBUG
>   #ifdef HPET_DEBUG
> @@ -41,6 +42,9 @@
>
>   #define HPET_MSI_SUPPORT        0
>
> +#define MAX_TICKS_NOT_ACCOUNTED     (uint64_t)500000000 /* 5 sec */
> +#define MAX_IRQ_RATE                (uint32_t)10
> +
>   struct HPETState;
>   typedef struct HPETTimer {  /* timers */
>       uint8_t tn;             /*timer number*/
> @@ -324,14 +328,35 @@ static const VMStateDescription vmstate_hpet = {
>       }
>   };
>
> +static void hpet_timer_driftfix_reset(HPETTimer *t)
> +{
> +    if (t->state->driftfix&&  timer_is_periodic(t)) {
> +        t->ticks_not_accounted = t->prev_period = t->period;
>    

This is rather confusing.  Clearly, ticks_not_accounted isn't actually 
ticks not accounted, it's a different variable entirely which is based 
on the current period.

If it were actually ticks_not_accounted, it should be reset to zero.

> +        t->irq_rate = 1;
> +        t->divisor = 1;
> +    }
> +}
> +
> +static bool hpet_timer_has_tick_backlog(HPETTimer *t)
> +{
> +    uint64_t backlog = 0;
> +
> +    if (t->ticks_not_accounted>= t->period + t->prev_period) {
> +        backlog = t->ticks_not_accounted - (t->period + t->prev_period);
> +    }
> +    return (backlog>= t->period);
> +}
> +
>   /*
>    * timer expiration callback
>    */
>   static void hpet_timer(void *opaque)
>   {
>       HPETTimer *t = opaque;
> +    HPETState *s = t->state;
>       uint64_t diff;
> -
> +    int irq_delivered = 0;
> +    uint32_t period_count = 0;
>       uint64_t period = t->period;
>       uint64_t cur_tick = hpet_get_ticks(t->state);
>
> @@ -339,13 +364,37 @@ static void hpet_timer(void *opaque)
>           if (t->config&  HPET_TN_32BIT) {
>               while (hpet_time_after(cur_tick, t->cmp)) {
>                   t->cmp = (uint32_t)(t->cmp + t->period);
> +                t->ticks_not_accounted += t->period;
> +                period_count++;
>               }
>           } else {
>               while (hpet_time_after64(cur_tick, t->cmp)) {
>                   t->cmp += period;
> +                t->ticks_not_accounted += period;
> +                period_count++;
>               }
>           }
>           diff = hpet_calculate_diff(t, cur_tick);
> +        if (s->driftfix) {
> +            if (t->ticks_not_accounted>  MAX_TICKS_NOT_ACCOUNTED) {
> +                t->ticks_not_accounted = t->period + t->prev_period;
> +            }
> +            if (hpet_timer_has_tick_backlog(t)) {
> +                if (t->irq_rate == 1 || period_count>  1) {
> +                    t->irq_rate++;
> +                    t->irq_rate = MIN(t->irq_rate, MAX_IRQ_RATE);
> +                }
> +                if (t->divisor == 0) {
> +                    assert(period_count);
> +                }
> +                if (period_count) {
> +                    t->divisor = t->irq_rate;
> +                }
> +                diff /= t->divisor--;
>    

Why subtracting from the divisor?  Shouldn't the divisor and irq_rate 
change in lockstep?

> +            } else {
> +                t->irq_rate = 1;
> +            }
> +        }
>           qemu_mod_timer(t->qemu_timer,
>                          qemu_get_clock_ns(vm_clock) + (int64_t)ticks_to_ns(diff));
>       } else if (t->config&  HPET_TN_32BIT&&  !timer_is_periodic(t)) {
> @@ -356,7 +405,22 @@ static void hpet_timer(void *opaque)
>               t->wrap_flag = 0;
>           }
>       }
> -    update_irq(t, 1);
> +    if (s->driftfix&&  timer_is_periodic(t)&&  period != 0) {
> +        if (t->ticks_not_accounted>= t->period + t->prev_period) {
> +            irq_delivered = update_irq(t, 1);
> +            if (irq_delivered) {
> +                t->ticks_not_accounted -= t->prev_period;
> +                t->prev_period = t->period;
> +            } else {
> +                if (period_count) {
> +                    t->irq_rate++;
> +                    t->irq_rate = MIN(t->irq_rate, MAX_IRQ_RATE);
> +                }
> +            }
> +        }
> +    } else {
> +        update_irq(t, 1);
> +    }
>   }
>    

Overall I think the intention is correct, and the code may be as well, 
but anything with this amount of subtle manipulations and math tricks 
needs to be more well commented for others to understand it.

I'll try to parse it again later, hopefully with better luck,

Zach

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH v4 5/5] hpet 'driftfix': add code in hpet_timer() to compensate delayed callbacks and coalesced interrupts
  2011-05-12  1:58     ` Zachary Amsden
@ 2011-05-12  9:48       ` Ulrich Obergfell
  -1 siblings, 0 replies; 16+ messages in thread
From: Ulrich Obergfell @ 2011-05-12  9:48 UTC (permalink / raw)
  To: Zachary Amsden
  Cc: qemu-devel, aliguori, kvm, jan kiszka, mtosatti, gcosta, avi


Hi Zachary,

1. re.:

>> +static void hpet_timer_driftfix_reset(HPETTimer *t)
>> +{
>> +    if (t->state->driftfix&&  timer_is_periodic(t)) {
>> +        t->ticks_not_accounted = t->prev_period = t->period;
>>    
>
> This is rather confusing.  Clearly, ticks_not_accounted isn't actually
> ticks not accounted, it's a different variable entirely which is based
> on the current period.
>
> If it were actually ticks_not_accounted, it should be reset to zero.


hpet_timer_driftfix_reset() is called at two sites before the periodic
timer is actually started via hpet_set_timer(). An interrupt shall be
delivered at the first callback of hpet_timer(), after t->period amount
of time has passed. The ticks are recorded as 'not accounted' until the
interrupt is delivered to the guest. The following message explains why
t->prev_period is needed in addition to t->period and how it is used.

  http://lists.gnu.org/archive/html/qemu-devel/2011-05/msg00275.html

On entry to hpet_timer(), t->ticks_not_accounted is >= t->prev_period,
and is it increased by t->period while the comparator register is being
advanced. This is also the place where we can detect whether the current
callback was delayed. If the period_count is > 1, the delay was so large
that we missed to inject an interrupt (which needs to be compensated).

             while (hpet_time_after(cur_tick, t->cmp)) {
                 t->cmp = (uint32_t)(t->cmp + t->period);
+                t->ticks_not_accounted += t->period;
+                period_count++;
             }

Please note that period_count can be zero. This happens during callbacks
that inject additional interrupts 'inside of' a period interval in order
to compensate missed and coalesced interrupts.

t->ticks_not_accounted is decreased only if an interrupt was delivered
to the guest. If an interrupt could not be delivered, the ticks that are
represented by that interrupt remain recorded as 'not accounted' (this
also triggers compensation of coalesced interrupts).

+            if (irq_delivered) {
+                t->ticks_not_accounted -= t->prev_period;
+                t->prev_period = t->period;
+            } else {
+                if (period_count) {
+                    t->irq_rate++;
+                    t->irq_rate = MIN(t->irq_rate, MAX_IRQ_RATE);
+                }
+            }


2. re.:

>> +                if (period_count) {
>> +                    t->divisor = t->irq_rate;
>> +                }
>> +                diff /= t->divisor--;
>>    
>
> Why subtracting from the divisor?  Shouldn't the divisor and irq_rate
> change in lockstep?


t->irq_rate is the rate at which interrupts are delivered to the guest,
relative to the period length. If t->irq_rate is 1, then one interrupt
shall be injected per period interval. If t->irq_rate is > 1, we are in
'compensation mode' (trying to inject additional interrupts 'inside of'
an interval).

- If t->irq_rate is 2, then two interrupts shall be injected during a
  period interval (one regular and one additional).

- If t->irq_rate is 3, then three interrupts shall be injected during a
  period interval (one regular and two additional).

- etc.

A non-zero period_count marks the start of an interval, at which the
divisor is set to t->irq_rate. Let's take a look at an example where
t->divisor = t->irq_rate = 3.

- The current period starts at t(p), the next period starts at t(p+1).
  We are now at t(p). The first additional interrupt shall be injected
  at a(1), the second at a(2). Hence, the next callback is scheduled
  to occur at a(1) = t(p) + diff / 3.

  t(p)                 a(1)                 a(2)                 t(p+1)
  +--------------------+--------------------+--------------------+ time
   <--------------------------- diff --------------------------->
   <---- diff / 3 ---->

- We are now in the callback at a(1). A new diff has been calculated,
  which is equal to the remaining time in the interval from a(1) to
  t(p+1). The second additional interrupt shall be injected at a(2).
  Hence, the next callback is scheduled to occur at a(2) = a(1) + diff / 2.

                       a(1)                 a(2)                 t(p+1)
                       +--------------------+--------------------+ time
                        <------------------ diff --------------->
                        <---- diff / 2 ---->

- We are now in the callback at a(2). A new diff has been calculated,
  which is equal to the remaining time in the interval from a(2) to
  t(p+1). The next callback marks the beginning of period t(p+1).

                                            a(2)                 t(p+1)
                                            +--------------------+ time
                                             <------ diff ------>
                                             <---- diff / 1 ---->

At t(p), the divisor is set to irq_rate (= 3). diff is divided by 3
and the divisor is decremented by one. At a(1), the divisor is 2.
diff is divided by 2 and the divisor is decremented by one. At a(2),
the divisor is 1. diff is divided by 1 and the divisor will be reset
at the beginning of t(p+1).


Regards,

Uli

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH v4 5/5] hpet 'driftfix': add code in hpet_timer() to compensate delayed callbacks and coalesced interrupts
@ 2011-05-12  9:48       ` Ulrich Obergfell
  0 siblings, 0 replies; 16+ messages in thread
From: Ulrich Obergfell @ 2011-05-12  9:48 UTC (permalink / raw)
  To: Zachary Amsden
  Cc: aliguori, kvm, jan kiszka, mtosatti, qemu-devel, gcosta, avi


Hi Zachary,

1. re.:

>> +static void hpet_timer_driftfix_reset(HPETTimer *t)
>> +{
>> +    if (t->state->driftfix&&  timer_is_periodic(t)) {
>> +        t->ticks_not_accounted = t->prev_period = t->period;
>>    
>
> This is rather confusing.  Clearly, ticks_not_accounted isn't actually
> ticks not accounted, it's a different variable entirely which is based
> on the current period.
>
> If it were actually ticks_not_accounted, it should be reset to zero.


hpet_timer_driftfix_reset() is called at two sites before the periodic
timer is actually started via hpet_set_timer(). An interrupt shall be
delivered at the first callback of hpet_timer(), after t->period amount
of time has passed. The ticks are recorded as 'not accounted' until the
interrupt is delivered to the guest. The following message explains why
t->prev_period is needed in addition to t->period and how it is used.

  http://lists.gnu.org/archive/html/qemu-devel/2011-05/msg00275.html

On entry to hpet_timer(), t->ticks_not_accounted is >= t->prev_period,
and is it increased by t->period while the comparator register is being
advanced. This is also the place where we can detect whether the current
callback was delayed. If the period_count is > 1, the delay was so large
that we missed to inject an interrupt (which needs to be compensated).

             while (hpet_time_after(cur_tick, t->cmp)) {
                 t->cmp = (uint32_t)(t->cmp + t->period);
+                t->ticks_not_accounted += t->period;
+                period_count++;
             }

Please note that period_count can be zero. This happens during callbacks
that inject additional interrupts 'inside of' a period interval in order
to compensate missed and coalesced interrupts.

t->ticks_not_accounted is decreased only if an interrupt was delivered
to the guest. If an interrupt could not be delivered, the ticks that are
represented by that interrupt remain recorded as 'not accounted' (this
also triggers compensation of coalesced interrupts).

+            if (irq_delivered) {
+                t->ticks_not_accounted -= t->prev_period;
+                t->prev_period = t->period;
+            } else {
+                if (period_count) {
+                    t->irq_rate++;
+                    t->irq_rate = MIN(t->irq_rate, MAX_IRQ_RATE);
+                }
+            }


2. re.:

>> +                if (period_count) {
>> +                    t->divisor = t->irq_rate;
>> +                }
>> +                diff /= t->divisor--;
>>    
>
> Why subtracting from the divisor?  Shouldn't the divisor and irq_rate
> change in lockstep?


t->irq_rate is the rate at which interrupts are delivered to the guest,
relative to the period length. If t->irq_rate is 1, then one interrupt
shall be injected per period interval. If t->irq_rate is > 1, we are in
'compensation mode' (trying to inject additional interrupts 'inside of'
an interval).

- If t->irq_rate is 2, then two interrupts shall be injected during a
  period interval (one regular and one additional).

- If t->irq_rate is 3, then three interrupts shall be injected during a
  period interval (one regular and two additional).

- etc.

A non-zero period_count marks the start of an interval, at which the
divisor is set to t->irq_rate. Let's take a look at an example where
t->divisor = t->irq_rate = 3.

- The current period starts at t(p), the next period starts at t(p+1).
  We are now at t(p). The first additional interrupt shall be injected
  at a(1), the second at a(2). Hence, the next callback is scheduled
  to occur at a(1) = t(p) + diff / 3.

  t(p)                 a(1)                 a(2)                 t(p+1)
  +--------------------+--------------------+--------------------+ time
   <--------------------------- diff --------------------------->
   <---- diff / 3 ---->

- We are now in the callback at a(1). A new diff has been calculated,
  which is equal to the remaining time in the interval from a(1) to
  t(p+1). The second additional interrupt shall be injected at a(2).
  Hence, the next callback is scheduled to occur at a(2) = a(1) + diff / 2.

                       a(1)                 a(2)                 t(p+1)
                       +--------------------+--------------------+ time
                        <------------------ diff --------------->
                        <---- diff / 2 ---->

- We are now in the callback at a(2). A new diff has been calculated,
  which is equal to the remaining time in the interval from a(2) to
  t(p+1). The next callback marks the beginning of period t(p+1).

                                            a(2)                 t(p+1)
                                            +--------------------+ time
                                             <------ diff ------>
                                             <---- diff / 1 ---->

At t(p), the divisor is set to irq_rate (= 3). diff is divided by 3
and the divisor is decremented by one. At a(1), the divisor is 2.
diff is divided by 2 and the divisor is decremented by one. At a(2),
the divisor is 1. diff is divided by 1 and the divisor will be reset
at the beginning of t(p+1).


Regards,

Uli

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2011-05-12  9:48 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-05-09  7:03 [PATCH v4 0/5] hpet 'driftfix': alleviate time drift with HPET periodic timers Ulrich Obergfell
2011-05-09  7:03 ` [Qemu-devel] " Ulrich Obergfell
2011-05-09  7:03 ` [PATCH v4 1/5] hpet 'driftfix': add hooks required to detect coalesced interrupts (x86 apic only) Ulrich Obergfell
2011-05-09  7:03   ` [Qemu-devel] " Ulrich Obergfell
2011-05-09  7:03 ` [PATCH v4 2/5] hpet 'driftfix': add driftfix property to HPETState and DeviceInfo Ulrich Obergfell
2011-05-09  7:03   ` [Qemu-devel] " Ulrich Obergfell
2011-05-09  7:03 ` [PATCH v4 3/5] hpet 'driftfix': add fields to HPETTimer and VMStateDescription Ulrich Obergfell
2011-05-09  7:03   ` [Qemu-devel] " Ulrich Obergfell
2011-05-09  7:03 ` [PATCH v4 4/5] hpet 'driftfix': add code in update_irq() to detect coalesced interrupts (x86 apic only) Ulrich Obergfell
2011-05-09  7:03   ` [Qemu-devel] " Ulrich Obergfell
2011-05-09  7:03 ` [PATCH v4 5/5] hpet 'driftfix': add code in hpet_timer() to compensate delayed callbacks and coalesced interrupts Ulrich Obergfell
2011-05-09  7:03   ` [Qemu-devel] " Ulrich Obergfell
2011-05-12  1:58   ` Zachary Amsden
2011-05-12  1:58     ` Zachary Amsden
2011-05-12  9:48     ` Ulrich Obergfell
2011-05-12  9:48       ` Ulrich Obergfell

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.