From: john stultz Modify the update_wall_time function so it increments time using the clocksource abstraction instead of jiffies. Since the only clocksource driver currently provided is the jiffies clocksource, this should result in no functional change. Additionally, a timekeeping_init and timekeeping_resume function has been added to initialize and maintain some of the new timekeping state. Signed-off-by: John Stultz Signed-off-by: Andrew Morton --- include/linux/time.h | 2 init/main.c | 1 kernel/Makefile | 1 kernel/timer.c | 93 +++++++++++++++++++++++++++++++++++------ 4 files changed, 84 insertions(+), 13 deletions(-) diff -puN include/linux/time.h~time-use-clocksource-infrastructure-for-update_wall_time include/linux/time.h --- devel/include/linux/time.h~time-use-clocksource-infrastructure-for-update_wall_time 2006-05-19 16:01:02.000000000 -0700 +++ devel-akpm/include/linux/time.h 2006-05-19 16:01:02.000000000 -0700 @@ -89,6 +89,8 @@ extern struct timespec xtime; extern struct timespec wall_to_monotonic; extern seqlock_t xtime_lock; +void timekeeping_init(void); + static inline unsigned long get_seconds(void) { return xtime.tv_sec; diff -puN init/main.c~time-use-clocksource-infrastructure-for-update_wall_time init/main.c --- devel/init/main.c~time-use-clocksource-infrastructure-for-update_wall_time 2006-05-19 16:01:02.000000000 -0700 +++ devel-akpm/init/main.c 2006-05-19 16:01:02.000000000 -0700 @@ -500,6 +500,7 @@ asmlinkage void __init start_kernel(void hrtimers_init(); softirq_init(); time_init(); + timekeeping_init(); /* * HACK ALERT! This is early. We're enabling the console before diff -puN kernel/Makefile~time-use-clocksource-infrastructure-for-update_wall_time kernel/Makefile --- devel/kernel/Makefile~time-use-clocksource-infrastructure-for-update_wall_time 2006-05-19 16:01:02.000000000 -0700 +++ devel-akpm/kernel/Makefile 2006-05-19 16:01:02.000000000 -0700 @@ -10,6 +10,7 @@ obj-y = sched.o fork.o exec_domain.o kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ hrtimer.o +obj-y += time/ obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o obj-$(CONFIG_FUTEX) += futex.o ifeq ($(CONFIG_COMPAT),y) diff -puN kernel/timer.c~time-use-clocksource-infrastructure-for-update_wall_time kernel/timer.c --- devel/kernel/timer.c~time-use-clocksource-infrastructure-for-update_wall_time 2006-05-19 16:01:02.000000000 -0700 +++ devel-akpm/kernel/timer.c 2006-05-19 16:01:02.000000000 -0700 @@ -792,24 +792,93 @@ u64 current_tick_length(void) return ((u64) delta_nsec << (SHIFT_SCALE - 10)) + time_adj; } +/* XXX - all of this timekeeping code should be later moved to time.c */ +#include +static struct clocksource *clock; /* pointer to current clocksource */ +static cycle_t last_clock_cycle; /* cycle value at last update_wall_time */ /* - * Using a loop looks inefficient, but "ticks" is - * usually just one (we shouldn't be losing ticks, - * we're doing this this way mainly for interrupt - * latency reasons, not because we think we'll - * have lots of lost timer ticks + * timekeeping_init - Initializes the clocksource and common timekeeping values */ -static void update_wall_time(unsigned long ticks) +void timekeeping_init(void) { - do { - ticks--; + unsigned long flags; + + write_seqlock_irqsave(&xtime_lock, flags); + clock = get_next_clocksource(); + calculate_clocksource_interval(clock, tick_nsec); + last_clock_cycle = read_clocksource(clock); + ntp_clear(); + write_sequnlock_irqrestore(&xtime_lock, flags); +} + + +/* + * timekeeping_resume - Resumes the generic timekeeping subsystem. + * @dev: unused + * + * This is for the generic clocksource timekeeping. + * xtime/wall_to_monotonic/jiffies/wall_jiffies/etc are + * still managed by arch specific suspend/resume code. + */ +static int timekeeping_resume(struct sys_device *dev) +{ + unsigned long flags; + + write_seqlock_irqsave(&xtime_lock, flags); + /* restart the last cycle value */ + last_clock_cycle = read_clocksource(clock); + write_sequnlock_irqrestore(&xtime_lock, flags); + return 0; +} + +/* sysfs resume/suspend bits for timekeeping */ +static struct sysdev_class timekeeping_sysclass = { + .resume = timekeeping_resume, + set_kset_name("timekeeping"), +}; + +static struct sys_device device_timer = { + .id = 0, + .cls = &timekeeping_sysclass, +}; + +static int timekeeping_init_device(void) +{ + int error = sysdev_class_register(&timekeeping_sysclass); + if (!error) + error = sysdev_register(&device_timer); + return error; +} + +device_initcall(timekeeping_init_device); + +/* + * update_wall_time - Uses the current clocksource to increment the wall time + * + * Called from the timer interrupt, must hold a write on xtime_lock. + */ +static void update_wall_time(void) +{ + cycle_t now, offset; + + now = read_clocksource(clock); + offset = (now - last_clock_cycle)&clock->mask; + + /* normally this loop will run just once, however in the + * case of lost or late ticks, it will accumulate correctly. + */ + while (offset > clock->interval_cycles) { + /* accumulate one interval */ + last_clock_cycle += clock->interval_cycles; + offset -= clock->interval_cycles; + update_wall_time_one_tick(); if (xtime.tv_nsec >= 1000000000) { xtime.tv_nsec -= 1000000000; xtime.tv_sec++; second_overflow(); } - } while (ticks); + } } /* @@ -915,10 +984,8 @@ static inline void update_times(void) unsigned long ticks; ticks = jiffies - wall_jiffies; - if (ticks) { - wall_jiffies += ticks; - update_wall_time(ticks); - } + wall_jiffies += ticks; + update_wall_time(); calc_load(ticks); } _