Suspend 2 merge: 20/51: Timer freezer (experimental).

Nigel Cunningham
2004-11-24T13:16:24Z

Here's experimental support for freezing timers. It doesn't really
freeze them, but rather reschedules the call for a little later. One
issue I need to think about is staggering the invocations at resume
time, as some of the per CPU timer code seeks to do.

There is debugging code in here so that when it's used (it's off by
default at the moment), and a timer that needs to be no_freeze holds up
I/O, the user can find out what it is. Other code allows the timer
freezer to be disabled on the fly (so you don't have to reboot because
of this).

(This could replace the patches above for MCE checking and slab
reaping).

diff -ruN 550-timer-freezer-old/drivers/block/ll_rw_blk.c
550-timer-freezer-new/drivers/block/ll_rw_blk.c
--- 550-timer-freezer-old/drivers/block/ll_rw_blk.c 2004-11-24
17:55:30.776567832 +1100
+++ 550-timer-freezer-new/drivers/block/ll_rw_blk.c 2004-11-24
17:23:36.145077968 +1100
@@ -254,6 +254,7 @@

q->unplug_timer.function = blk_unplug_timeout;
q->unplug_timer.data = (unsigned long)q;
+ q->unplug_timer.no_freeze = 1;

/*
* by default assume old behaviour and bounce for any highmem page
diff -ruN 550-timer-freezer-old/drivers/input/serio/i8042.c
550-timer-freezer-new/drivers/input/serio/i8042.c
--- 550-timer-freezer-old/drivers/input/serio/i8042.c 2004-11-24
09:52:58.000000000 +1100
+++ 550-timer-freezer-new/drivers/input/serio/i8042.c 2004-11-24
17:23:36.154076600 +1100
@@ -1039,6 +1039,7 @@
dbg_init();

init_timer(&i8042_timer);
+ i8042_timer.no_freeze = 1;
i8042_timer.function = i8042_timer_func;

if (i8042_platform_init())
diff -ruN 550-timer-freezer-old/include/linux/timer.h
550-timer-freezer-new/include/linux/timer.h
--- 550-timer-freezer-old/include/linux/timer.h 2004-11-03
21:54:17.000000000 +1100
+++ 550-timer-freezer-new/include/linux/timer.h 2004-11-24
17:23:36.169074320 +1100
@@ -19,6 +19,8 @@
unsigned long data;

struct tvec_t_base_s *base;
+
+ int no_freeze;
};

#define TIMER_MAGIC 0x4b87ad6e
diff -ruN 550-timer-freezer-old/kernel/timer.c
550-timer-freezer-new/kernel/timer.c
--- 550-timer-freezer-old/kernel/timer.c 2004-11-24 17:55:30.863554608
+1100
+++ 550-timer-freezer-new/kernel/timer.c 2004-11-24 17:55:22.021898744
+1100
@@ -31,6 +31,7 @@
#include <linux/time.h>
#include <linux/jiffies.h>
#include <linux/cpu.h>
+#include <linux/suspend.h>
#include <linux/syscalls.h>

#include <asm/uaccess.h>
@@ -429,6 +430,9 @@
*/
#define INDEX(N) (base->timer_jiffies >> (TVR_BITS + N * TVN_BITS)) &
TVN_MASK

+#define FN_CACHE_SIZE 15
+static void * recent_fns[FN_CACHE_SIZE];
+
static inline void __run_timers(tvec_base_t *base)
{
struct timer_list *timer;
@@ -463,7 +467,23 @@
smp_wmb();
timer->base = NULL;
spin_unlock_irq(&base->lock);
- fn(data);
+ if (unlikely(test_suspend_state(SUSPEND_TIMER_FREEZER_ON) &&
(!timer->no_freeze))) {
+ int shown = 0, i, copy_start = 0;
+ for (i = 0; i < FN_CACHE_SIZE; i++)
+ if (fn == recent_fns[i]) {
+ shown = i + 1;
+ copy_start = i;
+ break;
+ }
+ for (i = copy_start; i < (FN_CACHE_SIZE - 1); i++)
+ recent_fns[i] = recent_fns[i+1];
+ recent_fns[FN_CACHE_SIZE - 1] = fn;
+ if (!shown) {
+ printk("Timed call of %p delayed while freezer on.\n", fn);
+ }
+ mod_timer(timer, jiffies + HZ);
+ } else
+ fn(data);
spin_lock_irq(&base->lock);
goto repeat;
}
diff -ruN 550-timer-freezer-old/net/sched/sch_generic.c
550-timer-freezer-new/net/sched/sch_generic.c
--- 550-timer-freezer-old/net/sched/sch_generic.c 2004-11-24
09:53:13.000000000 +1100
+++ 550-timer-freezer-new/net/sched/sch_generic.c 2004-11-24
17:23:36.189071280 +1100
@@ -210,6 +210,7 @@
init_timer(&dev->watchdog_timer);
dev->watchdog_timer.data = (unsigned long)dev;
dev->watchdog_timer.function = dev_watchdog;
+ dev->watchdog_timer.no_freeze = 1;
}

void __netdev_watchdog_up(struct net_device *dev)