]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
timer fixes for .26
authorGreg Kroah-Hartman <gregkh@suse.de>
Thu, 2 Oct 2008 00:33:17 +0000 (17:33 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 2 Oct 2008 00:33:17 +0000 (17:33 -0700)
12 files changed:
queue-2.6.26/series
queue-2.6.26/timer-fix-01.patch [new file with mode: 0644]
queue-2.6.26/timer-fix-02.patch [new file with mode: 0644]
queue-2.6.26/timer-fix-03.patch [new file with mode: 0644]
queue-2.6.26/timer-fix-04.patch [new file with mode: 0644]
queue-2.6.26/timer-fix-05.patch [new file with mode: 0644]
queue-2.6.26/timer-fix-06.patch [new file with mode: 0644]
queue-2.6.26/timer-fix-07.patch [new file with mode: 0644]
queue-2.6.26/timer-fix-08.patch [new file with mode: 0644]
queue-2.6.26/timer-fix-09.patch [new file with mode: 0644]
queue-2.6.26/timer-fix-10.patch [new file with mode: 0644]
queue-2.6.26/timer-fix-11.patch [new file with mode: 0644]

index bd4f093edac19ea2f35bba83502c7f69da808531..41c7c28a807f0f8331011e66c67cdbcb5e5f79f3 100644 (file)
@@ -37,3 +37,14 @@ x86-fix-smp-alternatives-use-mutex-instead-of-spinlock-text_poke-is-sleepable.pa
 acpi-avoid-bogus-ec-timeout-when-ec-is-in-polling-mode.patch
 x86-add-io-delay-quirk-for-presario-f700.patch
 x86-fix-memmap-exactmap-boot-argument.patch
+timer-fix-01.patch
+timer-fix-02.patch
+timer-fix-03.patch
+timer-fix-04.patch
+timer-fix-05.patch
+timer-fix-06.patch
+timer-fix-07.patch
+timer-fix-08.patch
+timer-fix-09.patch
+timer-fix-10.patch
+timer-fix-11.patch
diff --git a/queue-2.6.26/timer-fix-01.patch b/queue-2.6.26/timer-fix-01.patch
new file mode 100644 (file)
index 0000000..38290b0
--- /dev/null
@@ -0,0 +1,84 @@
+From 7c1e76897492d92b6a1c2d6892494d39ded9680c Mon Sep 17 00:00:00 2001
+From: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
+Date: Wed, 3 Sep 2008 21:36:50 +0000
+Subject: clockevents: prevent clockevent event_handler ending up handler_noop
+
+From: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
+
+commit 7c1e76897492d92b6a1c2d6892494d39ded9680c upstream
+
+There is a ordering related problem with clockevents code, due to which
+clockevents_register_device() called after tickless/highres switch
+will not work. The new clockevent ends up with clockevents_handle_noop as
+event handler, resulting in no timer activity.
+
+The problematic path seems to be
+
+* old device already has hrtimer_interrupt as the event_handler
+* new clockevent device registers with a higher rating
+* tick_check_new_device() is called
+  * clockevents_exchange_device() gets called
+    * old->event_handler is set to clockevents_handle_noop
+  * tick_setup_device() is called for the new device
+    * which sets new->event_handler using the old->event_handler which is noop.
+
+Change the ordering so that new device inherits the proper handler.
+
+This does not have any issue in normal case as most likely all the clockevent
+devices are setup before the highres switch. But, can potentially be affecting
+some corner case where HPET force detect happens after the highres switch.
+This was a problem with HPET in MSI mode code that we have been experimenting
+with.
+
+Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
+Signed-off-by: Shaohua Li <shaohua.li@intel.com>
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Ingo Molnar <mingo@elte.hu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ include/linux/clockchips.h |    2 ++
+ kernel/time/clockevents.c  |    3 +--
+ kernel/time/tick-common.c  |    1 +
+ 3 files changed, 4 insertions(+), 2 deletions(-)
+
+--- a/include/linux/clockchips.h
++++ b/include/linux/clockchips.h
+@@ -127,6 +127,8 @@ extern int clockevents_register_notifier
+ extern int clockevents_program_event(struct clock_event_device *dev,
+                                    ktime_t expires, ktime_t now);
++extern void clockevents_handle_noop(struct clock_event_device *dev);
++
+ #ifdef CONFIG_GENERIC_CLOCKEVENTS
+ extern void clockevents_notify(unsigned long reason, void *arg);
+ #else
+--- a/kernel/time/clockevents.c
++++ b/kernel/time/clockevents.c
+@@ -177,7 +177,7 @@ void clockevents_register_device(struct 
+ /*
+  * Noop handler when we shut down an event device
+  */
+-static void clockevents_handle_noop(struct clock_event_device *dev)
++void clockevents_handle_noop(struct clock_event_device *dev)
+ {
+ }
+@@ -199,7 +199,6 @@ void clockevents_exchange_device(struct 
+        * released list and do a notify add later.
+        */
+       if (old) {
+-              old->event_handler = clockevents_handle_noop;
+               clockevents_set_mode(old, CLOCK_EVT_MODE_UNUSED);
+               list_del(&old->list);
+               list_add(&old->list, &clockevents_released);
+--- a/kernel/time/tick-common.c
++++ b/kernel/time/tick-common.c
+@@ -161,6 +161,7 @@ static void tick_setup_device(struct tic
+       } else {
+               handler = td->evtdev->event_handler;
+               next_event = td->evtdev->next_event;
++              td->evtdev->event_handler = clockevents_handle_noop;
+       }
+       td->evtdev = newdev;
diff --git a/queue-2.6.26/timer-fix-02.patch b/queue-2.6.26/timer-fix-02.patch
new file mode 100644 (file)
index 0000000..316d4bf
--- /dev/null
@@ -0,0 +1,52 @@
+From d4496b39559c6d43f83e4c08b899984f8b8089b5 Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Wed, 3 Sep 2008 21:36:57 +0000
+Subject: clockevents: prevent endless loop in periodic broadcast handler
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit d4496b39559c6d43f83e4c08b899984f8b8089b5 upstream
+
+The reprogramming of the periodic broadcast handler was broken,
+when the first programming returned -ETIME. The clockevents code
+stores the new expiry value in the clock events device next_event field
+only when the programming time has not been elapsed yet. The loop in
+question calculates the new expiry value from the next_event value
+and therefor never increases.
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Ingo Molnar <mingo@elte.hu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ kernel/time/tick-broadcast.c |   11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+--- a/kernel/time/tick-broadcast.c
++++ b/kernel/time/tick-broadcast.c
+@@ -174,6 +174,8 @@ static void tick_do_periodic_broadcast(v
+  */
+ static void tick_handle_periodic_broadcast(struct clock_event_device *dev)
+ {
++      ktime_t next;
++
+       tick_do_periodic_broadcast();
+       /*
+@@ -184,10 +186,13 @@ static void tick_handle_periodic_broadca
+       /*
+        * Setup the next period for devices, which do not have
+-       * periodic mode:
++       * periodic mode. We read dev->next_event first and add to it
++       * when the event alrady expired. clockevents_program_event()
++       * sets dev->next_event only when the event is really
++       * programmed to the device.
+        */
+-      for (;;) {
+-              ktime_t next = ktime_add(dev->next_event, tick_period);
++      for (next = dev->next_event; ;) {
++              next = ktime_add(next, tick_period);
+               if (!clockevents_program_event(dev, next, ktime_get()))
+                       return;
diff --git a/queue-2.6.26/timer-fix-03.patch b/queue-2.6.26/timer-fix-03.patch
new file mode 100644 (file)
index 0000000..e8d29b7
--- /dev/null
@@ -0,0 +1,67 @@
+From 7205656ab48da29a95d7f55e43a81db755d3cb3a Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Wed, 3 Sep 2008 21:37:03 +0000
+Subject: clockevents: enforce reprogram in oneshot setup
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit 7205656ab48da29a95d7f55e43a81db755d3cb3a upstream
+
+In tick_oneshot_setup we program the device to the given next_event,
+but we do not check the return value. We need to make sure that the
+device is programmed enforced so the interrupt handler engine starts
+working. Split out the reprogramming function from tick_program_event()
+and call it with the device, which was handed in to tick_setup_oneshot().
+Set the force argument, so the devices is firing an interrupt.
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Ingo Molnar <mingo@elte.hu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ kernel/time/tick-oneshot.c |   18 ++++++++++++++----
+ 1 file changed, 14 insertions(+), 4 deletions(-)
+
+--- a/kernel/time/tick-oneshot.c
++++ b/kernel/time/tick-oneshot.c
+@@ -23,11 +23,11 @@
+ #include "tick-internal.h"
+ /**
+- * tick_program_event
++ * tick_program_event internal worker function
+  */
+-int tick_program_event(ktime_t expires, int force)
++static int __tick_program_event(struct clock_event_device *dev,
++                              ktime_t expires, int force)
+ {
+-      struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev;
+       ktime_t now = ktime_get();
+       while (1) {
+@@ -41,6 +41,16 @@ int tick_program_event(ktime_t expires, 
+ }
+ /**
++ * tick_program_event
++ */
++int tick_program_event(ktime_t expires, int force)
++{
++      struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev;
++
++      return __tick_program_event(dev, expires, force);
++}
++
++/**
+  * tick_resume_onshot - resume oneshot mode
+  */
+ void tick_resume_oneshot(void)
+@@ -61,7 +71,7 @@ void tick_setup_oneshot(struct clock_eve
+ {
+       newdev->event_handler = handler;
+       clockevents_set_mode(newdev, CLOCK_EVT_MODE_ONESHOT);
+-      clockevents_program_event(newdev, next_event, ktime_get());
++      __tick_program_event(newdev, next_event, 1);
+ }
+ /**
diff --git a/queue-2.6.26/timer-fix-04.patch b/queue-2.6.26/timer-fix-04.patch
new file mode 100644 (file)
index 0000000..994ff0e
--- /dev/null
@@ -0,0 +1,74 @@
+From 9c17bcda991000351cb2373f78be7e4b1c44caa3 Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Wed, 3 Sep 2008 21:37:08 +0000
+Subject: clockevents: prevent multiple init/shutdown
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit 9c17bcda991000351cb2373f78be7e4b1c44caa3 upstream
+
+While chasing the C1E/HPET bugreports I went through the clock events
+code inch by inch and found that the broadcast device can be initialized
+and shutdown multiple times. Multiple shutdowns are not critical, but
+useless waste of time. Multiple initializations are simply broken. Another
+CPU might have the device in use already after the first initialization and
+the second init could just render it unusable again.
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Ingo Molnar <mingo@elte.hu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ kernel/time/tick-broadcast.c |   20 +++++++++++++-------
+ 1 file changed, 13 insertions(+), 7 deletions(-)
+
+--- a/kernel/time/tick-broadcast.c
++++ b/kernel/time/tick-broadcast.c
+@@ -209,7 +209,7 @@ static void tick_do_broadcast_on_off(voi
+       struct clock_event_device *bc, *dev;
+       struct tick_device *td;
+       unsigned long flags, *reason = why;
+-      int cpu;
++      int cpu, bc_stopped;
+       spin_lock_irqsave(&tick_broadcast_lock, flags);
+@@ -227,6 +227,8 @@ static void tick_do_broadcast_on_off(voi
+       if (!tick_device_is_functional(dev))
+               goto out;
++      bc_stopped = cpus_empty(tick_broadcast_mask);
++
+       switch (*reason) {
+       case CLOCK_EVT_NOTIFY_BROADCAST_ON:
+       case CLOCK_EVT_NOTIFY_BROADCAST_FORCE:
+@@ -248,9 +250,10 @@ static void tick_do_broadcast_on_off(voi
+               break;
+       }
+-      if (cpus_empty(tick_broadcast_mask))
+-              clockevents_set_mode(bc, CLOCK_EVT_MODE_SHUTDOWN);
+-      else {
++      if (cpus_empty(tick_broadcast_mask)) {
++              if (!bc_stopped)
++                      clockevents_set_mode(bc, CLOCK_EVT_MODE_SHUTDOWN);
++      } else if (bc_stopped) {
+               if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC)
+                       tick_broadcast_start_periodic(bc);
+               else
+@@ -500,9 +503,12 @@ static void tick_broadcast_clear_oneshot
+  */
+ void tick_broadcast_setup_oneshot(struct clock_event_device *bc)
+ {
+-      bc->event_handler = tick_handle_oneshot_broadcast;
+-      clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT);
+-      bc->next_event.tv64 = KTIME_MAX;
++      /* Set it up only once ! */
++      if (bc->event_handler != tick_handle_oneshot_broadcast) {
++              bc->event_handler = tick_handle_oneshot_broadcast;
++              clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT);
++              bc->next_event.tv64 = KTIME_MAX;
++      }
+ }
+ /*
diff --git a/queue-2.6.26/timer-fix-05.patch b/queue-2.6.26/timer-fix-05.patch
new file mode 100644 (file)
index 0000000..4fc4540
--- /dev/null
@@ -0,0 +1,129 @@
+From 1fb9b7d29d8e85ba3196eaa7ab871bf76fc98d36 Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Wed, 3 Sep 2008 21:37:14 +0000
+Subject: [PATCH] clockevents: prevent endless loop lockup
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit 1fb9b7d29d8e85ba3196eaa7ab871bf76fc98d36 upstream
+
+The C1E/HPET bug reports on AMDX2/RS690 systems where tracked down to a
+too small value of the HPET minumum delta for programming an event.
+
+The clockevents code needs to enforce an interrupt event on the clock event
+device in some cases. The enforcement code was stupid and naive, as it just
+added the minimum delta to the current time and tried to reprogram the device.
+When the minimum delta is too small, then this loops forever.
+
+Add a sanity check. Allow reprogramming to fail 3 times, then print a warning
+and double the minimum delta value to make sure, that this does not happen again.
+Use the same function for both tick-oneshot and tick-broadcast code.
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Ingo Molnar <mingo@elte.hu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ kernel/time/tick-broadcast.c |   10 +---------
+ kernel/time/tick-internal.h  |    2 ++
+ kernel/time/tick-oneshot.c   |   36 ++++++++++++++++++++++++++++++------
+ 3 files changed, 33 insertions(+), 15 deletions(-)
+
+--- a/kernel/time/tick-broadcast.c
++++ b/kernel/time/tick-broadcast.c
+@@ -370,16 +370,8 @@ cpumask_t *tick_get_broadcast_oneshot_ma
+ static int tick_broadcast_set_event(ktime_t expires, int force)
+ {
+       struct clock_event_device *bc = tick_broadcast_device.evtdev;
+-      ktime_t now = ktime_get();
+-      int res;
+-      for(;;) {
+-              res = clockevents_program_event(bc, expires, now);
+-              if (!res || !force)
+-                      return res;
+-              now = ktime_get();
+-              expires = ktime_add(now, ktime_set(0, bc->min_delta_ns));
+-      }
++      return tick_dev_program_event(bc, expires, force);
+ }
+ int tick_resume_broadcast_oneshot(struct clock_event_device *bc)
+--- a/kernel/time/tick-internal.h
++++ b/kernel/time/tick-internal.h
+@@ -17,6 +17,8 @@ extern void tick_handle_periodic(struct 
+ extern void tick_setup_oneshot(struct clock_event_device *newdev,
+                              void (*handler)(struct clock_event_device *),
+                              ktime_t nextevt);
++extern int tick_dev_program_event(struct clock_event_device *dev,
++                                ktime_t expires, int force);
+ extern int tick_program_event(ktime_t expires, int force);
+ extern void tick_oneshot_notify(void);
+ extern int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *));
+--- a/kernel/time/tick-oneshot.c
++++ b/kernel/time/tick-oneshot.c
+@@ -25,18 +25,42 @@
+ /**
+  * tick_program_event internal worker function
+  */
+-static int __tick_program_event(struct clock_event_device *dev,
+-                              ktime_t expires, int force)
++int tick_dev_program_event(struct clock_event_device *dev, ktime_t expires,
++                         int force)
+ {
+       ktime_t now = ktime_get();
++      int i;
+-      while (1) {
++      for (i = 0;;) {
+               int ret = clockevents_program_event(dev, expires, now);
+               if (!ret || !force)
+                       return ret;
++
++              /*
++               * We tried 2 times to program the device with the given
++               * min_delta_ns. If that's not working then we double it
++               * and emit a warning.
++               */
++              if (++i > 2) {
++                      printk(KERN_WARNING "CE: __tick_program_event of %s is "
++                             "stuck %llx %llx\n", dev->name ? dev->name : "?",
++                             now.tv64, expires.tv64);
++                      printk(KERN_WARNING
++                             "CE: increasing min_delta_ns %ld to %ld nsec\n",
++                             dev->min_delta_ns, dev->min_delta_ns << 1);
++                      WARN_ON(1);
++
++                      /* Double the min. delta and try again */
++                      if (!dev->min_delta_ns)
++                              dev->min_delta_ns = 5000;
++                      else
++                              dev->min_delta_ns <<= 1;
++                      i = 0;
++              }
++
+               now = ktime_get();
+-              expires = ktime_add(now, ktime_set(0, dev->min_delta_ns));
++              expires = ktime_add_ns(now, dev->min_delta_ns);
+       }
+ }
+@@ -47,7 +71,7 @@ int tick_program_event(ktime_t expires, 
+ {
+       struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev;
+-      return __tick_program_event(dev, expires, force);
++      return tick_dev_program_event(dev, expires, force);
+ }
+ /**
+@@ -71,7 +95,7 @@ void tick_setup_oneshot(struct clock_eve
+ {
+       newdev->event_handler = handler;
+       clockevents_set_mode(newdev, CLOCK_EVT_MODE_ONESHOT);
+-      __tick_program_event(newdev, next_event, 1);
++      tick_dev_program_event(newdev, next_event, 1);
+ }
+ /**
diff --git a/queue-2.6.26/timer-fix-06.patch b/queue-2.6.26/timer-fix-06.patch
new file mode 100644 (file)
index 0000000..8739bda
--- /dev/null
@@ -0,0 +1,40 @@
+From 7cfb0435330364f90f274a26ecdc5f47f738498c Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Wed, 3 Sep 2008 21:37:24 +0000
+Subject: HPET: make minimum reprogramming delta useful
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit 7cfb0435330364f90f274a26ecdc5f47f738498c upstream
+
+The minimum reprogramming delta was hardcoded in HPET ticks,
+which is stupid as it does not work with faster running HPETs.
+The C1E idle patches made this prominent on AMD/RS690 chipsets,
+where the HPET runs with 25MHz. Set it to 5us which seems to be
+a reasonable value and fixes the problems on the bug reporters
+machines. We have a further sanity check now in the clock events,
+which increases the delta when it is not sufficient.
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Tested-by: Luiz Fernando N. Capitulino <lcapitulino@mandriva.com.br>
+Tested-by: Dmitry Nezhevenko <dion@inhex.net>
+Signed-off-by: Ingo Molnar <mingo@elte.hu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ arch/x86/kernel/hpet.c |    4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/arch/x86/kernel/hpet.c
++++ b/arch/x86/kernel/hpet.c
+@@ -223,8 +223,8 @@ static void hpet_legacy_clockevent_regis
+       /* Calculate the min / max delta */
+       hpet_clockevent.max_delta_ns = clockevent_delta2ns(0x7FFFFFFF,
+                                                          &hpet_clockevent);
+-      hpet_clockevent.min_delta_ns = clockevent_delta2ns(0x30,
+-                                                         &hpet_clockevent);
++      /* 5 usec minimum reprogramming delta. */
++      hpet_clockevent.min_delta_ns = 5000;
+       /*
+        * Start hpet with the boot cpu mask and make it
diff --git a/queue-2.6.26/timer-fix-07.patch b/queue-2.6.26/timer-fix-07.patch
new file mode 100644 (file)
index 0000000..426e3fc
--- /dev/null
@@ -0,0 +1,76 @@
+From 7300711e8c6824fcfbd42a126980ff50439d8dd0 Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Sat, 6 Sep 2008 03:01:45 +0200
+Subject: clockevents: broadcast fixup possible waiters
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit 7300711e8c6824fcfbd42a126980ff50439d8dd0 upstream
+
+Until the C1E patches arrived there where no users of periodic broadcast
+before switching to oneshot mode. Now we need to trigger a possible
+waiter for a periodic broadcast when switching to oneshot mode.
+Otherwise we can starve them for ever.
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ kernel/time/tick-broadcast.c |   37 ++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 36 insertions(+), 1 deletion(-)
+
+--- a/kernel/time/tick-broadcast.c
++++ b/kernel/time/tick-broadcast.c
+@@ -490,6 +490,18 @@ static void tick_broadcast_clear_oneshot
+       cpu_clear(cpu, tick_broadcast_oneshot_mask);
+ }
++static void tick_broadcast_init_next_event(cpumask_t *mask, ktime_t expires)
++{
++      struct tick_device *td;
++      int cpu;
++
++      for_each_cpu_mask_nr(cpu, *mask) {
++              td = &per_cpu(tick_cpu_device, cpu);
++              if (td->evtdev)
++                      td->evtdev->next_event = expires;
++      }
++}
++
+ /**
+  * tick_broadcast_setup_oneshot - setup the broadcast device
+  */
+@@ -497,9 +509,32 @@ void tick_broadcast_setup_oneshot(struct
+ {
+       /* Set it up only once ! */
+       if (bc->event_handler != tick_handle_oneshot_broadcast) {
++              int was_periodic = bc->mode == CLOCK_EVT_MODE_PERIODIC;
++              int cpu = smp_processor_id();
++              cpumask_t mask;
++
+               bc->event_handler = tick_handle_oneshot_broadcast;
+               clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT);
+-              bc->next_event.tv64 = KTIME_MAX;
++
++              /* Take the do_timer update */
++              tick_do_timer_cpu = cpu;
++
++              /*
++               * We must be careful here. There might be other CPUs
++               * waiting for periodic broadcast. We need to set the
++               * oneshot_mask bits for those and program the
++               * broadcast device to fire.
++               */
++              mask = tick_broadcast_mask;
++              cpu_clear(cpu, mask);
++              cpus_or(tick_broadcast_oneshot_mask,
++                      tick_broadcast_oneshot_mask, mask);
++
++              if (was_periodic && !cpus_empty(mask)) {
++                      tick_broadcast_init_next_event(&mask, tick_next_period);
++                      tick_broadcast_set_event(tick_next_period, 1);
++              } else
++                      bc->next_event.tv64 = KTIME_MAX;
+       }
+ }
diff --git a/queue-2.6.26/timer-fix-08.patch b/queue-2.6.26/timer-fix-08.patch
new file mode 100644 (file)
index 0000000..d9188ee
--- /dev/null
@@ -0,0 +1,54 @@
+From f7676254f179eac6b5244a80195ec8ae0e9d4606 Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Sat, 6 Sep 2008 03:03:32 +0200
+Subject: x86: HPET fix moronic 32/64bit thinko
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit f7676254f179eac6b5244a80195ec8ae0e9d4606 upstream
+
+We use the HPET only in 32bit mode because:
+1) some HPETs are 32bit only
+2) on i386 there is no way to read/write the HPET atomic 64bit wide
+
+The HPET code unification done by the "moron of the year" did
+not take into account that unsigned long is different on 32 and
+64 bit.
+
+This thinko results in a possible endless loop in the clockevents
+code, when the return comparison fails due to the 64bit/332bit
+unawareness.
+
+unsigned long cnt = (u32) hpet_read() + delta can wrap over 32bit.
+but the final compare will fail and return -ETIME causing endless
+loops.
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ arch/x86/kernel/hpet.c |    8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/arch/x86/kernel/hpet.c
++++ b/arch/x86/kernel/hpet.c
+@@ -283,15 +283,15 @@ static void hpet_legacy_set_mode(enum cl
+ }
+ static int hpet_legacy_next_event(unsigned long delta,
+-                         struct clock_event_device *evt)
++                                struct clock_event_device *evt)
+ {
+-      unsigned long cnt;
++      u32 cnt;
+       cnt = hpet_readl(HPET_COUNTER);
+-      cnt += delta;
++      cnt += (u32) delta;
+       hpet_writel(cnt, HPET_T0_CMP);
+-      return ((long)(hpet_readl(HPET_COUNTER) - cnt ) > 0) ? -ETIME : 0;
++      return (s32)((u32)hpet_readl(HPET_COUNTER) - cnt) >= 0 ? -ETIME : 0;
+ }
+ /*
diff --git a/queue-2.6.26/timer-fix-09.patch b/queue-2.6.26/timer-fix-09.patch
new file mode 100644 (file)
index 0000000..6f4d1a4
--- /dev/null
@@ -0,0 +1,38 @@
+From 72d43d9bc9210d24d09202eaf219eac09e17b339 Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Sat, 6 Sep 2008 03:06:08 +0200
+Subject: x86: HPET: read back compare register before reading counter
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit 72d43d9bc9210d24d09202eaf219eac09e17b339 upstream
+
+After fixing the u32 thinko I sill had occasional hickups on ATI chipsets
+with small deltas. There seems to be a delay between writing the compare
+register and the transffer to the internal register which triggers the
+interrupt. Reading back the value makes sure, that it hit the internal
+match register befor we compare against the counter value.
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ arch/x86/kernel/hpet.c |    7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/arch/x86/kernel/hpet.c
++++ b/arch/x86/kernel/hpet.c
+@@ -291,6 +291,13 @@ static int hpet_legacy_next_event(unsign
+       cnt += (u32) delta;
+       hpet_writel(cnt, HPET_T0_CMP);
++      /*
++       * We need to read back the CMP register to make sure that
++       * what we wrote hit the chip before we compare it to the
++       * counter.
++       */
++      WARN_ON((u32)hpet_readl(HPET_T0_CMP) != cnt);
++
+       return (s32)((u32)hpet_readl(HPET_COUNTER) - cnt) >= 0 ? -ETIME : 0;
+ }
diff --git a/queue-2.6.26/timer-fix-10.patch b/queue-2.6.26/timer-fix-10.patch
new file mode 100644 (file)
index 0000000..8f44c56
--- /dev/null
@@ -0,0 +1,57 @@
+From 4ff4b9e19a80b73959ebeb28d1df40176686f0a8 Mon Sep 17 00:00:00 2001
+From: Maciej W. Rozycki <macro@linux-mips.org>
+Date: Fri, 5 Sep 2008 14:05:31 -0700
+Subject: ntp: fix calculation of the next jiffie to trigger RTC sync
+
+From: Maciej W. Rozycki <macro@linux-mips.org>
+
+commit 4ff4b9e19a80b73959ebeb28d1df40176686f0a8 upstream
+
+We have a bug in the calculation of the next jiffie to trigger the RTC
+synchronisation.  The aim here is to run sync_cmos_clock() as close as
+possible to the middle of a second.  Which means we want this function to
+be called less than or equal to half a jiffie away from when now.tv_nsec
+equals 5e8 (500000000).
+
+If this is not the case for a given call to the function, for this purpose
+instead of updating the RTC we calculate the offset in nanoseconds to the
+next point in time where now.tv_nsec will be equal 5e8.  The calculated
+offset is then converted to jiffies as these are the unit used by the
+timer.
+
+Hovewer timespec_to_jiffies() used here uses a ceil()-type rounding mode,
+where the resulting value is rounded up.  As a result the range of
+now.tv_nsec when the timer will trigger is from 5e8 to 5e8 + TICK_NSEC
+rather than the desired 5e8 - TICK_NSEC / 2 to 5e8 + TICK_NSEC / 2.
+
+As a result if for example sync_cmos_clock() happens to be called at the
+time when now.tv_nsec is between 5e8 + TICK_NSEC / 2 and 5e8 to 5e8 +
+TICK_NSEC, it will simply be rescheduled HZ jiffies later, falling in the
+same range of now.tv_nsec again.  Similarly for cases offsetted by an
+integer multiple of TICK_NSEC.
+
+This change addresses the problem by subtracting TICK_NSEC / 2 from the
+nanosecond offset to the next point in time where now.tv_nsec will be
+equal 5e8, effectively shifting the following rounding in
+timespec_to_jiffies() so that it produces a rounded-to-nearest result.
+
+Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Ingo Molnar <mingo@elte.hu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ kernel/time/ntp.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/kernel/time/ntp.c
++++ b/kernel/time/ntp.c
+@@ -245,7 +245,7 @@ static void sync_cmos_clock(unsigned lon
+       if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec / 2)
+               fail = update_persistent_clock(now);
+-      next.tv_nsec = (NSEC_PER_SEC / 2) - now.tv_nsec;
++      next.tv_nsec = (NSEC_PER_SEC / 2) - now.tv_nsec - (TICK_NSEC / 2);
+       if (next.tv_nsec <= 0)
+               next.tv_nsec += NSEC_PER_SEC;
diff --git a/queue-2.6.26/timer-fix-11.patch b/queue-2.6.26/timer-fix-11.patch
new file mode 100644 (file)
index 0000000..860053c
--- /dev/null
@@ -0,0 +1,57 @@
+From 61c22c34c6f80a8e89cff5ff717627c54cc14fd4 Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Tue, 9 Sep 2008 21:38:57 +0200
+Subject: clockevents: remove WARN_ON which was used to gather information
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit 61c22c34c6f80a8e89cff5ff717627c54cc14fd4 upstream
+
+The issue of the endless reprogramming loop due to a too small
+min_delta_ns was fixed with the previous updates of the clock events
+code, but we had no information about the spread of this problem. I
+added a WARN_ON to get automated information via kerneloops.org and to
+get some direct reports, which allowed me to analyse the affected
+machines.
+
+The WARN_ON has served its purpose and would be annoying for a release
+kernel. Remove it and just keep the information about the increase of
+the min_delta_ns value.
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ kernel/time/tick-oneshot.c |   18 ++++++++----------
+ 1 file changed, 8 insertions(+), 10 deletions(-)
+
+--- a/kernel/time/tick-oneshot.c
++++ b/kernel/time/tick-oneshot.c
+@@ -43,19 +43,17 @@ int tick_dev_program_event(struct clock_
+                * and emit a warning.
+                */
+               if (++i > 2) {
+-                      printk(KERN_WARNING "CE: __tick_program_event of %s is "
+-                             "stuck %llx %llx\n", dev->name ? dev->name : "?",
+-                             now.tv64, expires.tv64);
+-                      printk(KERN_WARNING
+-                             "CE: increasing min_delta_ns %ld to %ld nsec\n",
+-                             dev->min_delta_ns, dev->min_delta_ns << 1);
+-                      WARN_ON(1);
+-
+-                      /* Double the min. delta and try again */
++                      /* Increase the min. delta and try again */
+                       if (!dev->min_delta_ns)
+                               dev->min_delta_ns = 5000;
+                       else
+-                              dev->min_delta_ns <<= 1;
++                              dev->min_delta_ns += dev->min_delta_ns >> 1;
++
++                      printk(KERN_WARNING
++                             "CE: %s increasing min_delta_ns to %lu nsec\n",
++                             dev->name ? dev->name : "?",
++                             dev->min_delta_ns << 1);
++
+                       i = 0;
+               }