diff --git a/include/linux/time.h b/include/linux/time.h index 0cd696cee998c0ed24d28ead62335160bc4e47a0..88d3b812841ec2186821e41abc9495611b9c9c67 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -77,6 +77,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 --git a/init/main.c b/init/main.c index f715b9b897538cb04dc94fa900bd8c3f5d6e26e7..9a970d317ea509bd010cab1411c676c15c0f1907 100644 --- a/init/main.c +++ b/init/main.c @@ -490,6 +490,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 --git a/kernel/Makefile b/kernel/Makefile index f6ef00f4f90fb9069982c1ef6c986d8bb4e7e346..bc4b8a7161ff4e1898d9a9632c930b99fd450044 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -10,6 +10,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.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 --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 95dd2200a1093d6a0a9e6796b6ed8cef616fe125..4288bfa12c3f03c2645b0fa9caf5747d0b982302 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -56,7 +56,7 @@ static int finished_booting; * * Hack to avoid lots of clocksource churn at boot time */ -static int clocksource_done_booting(void) +static int __init clocksource_done_booting(void) { finished_booting = 1; return 0; @@ -289,7 +289,7 @@ static struct sys_device device_clocksource = { .cls = &clocksource_sysclass, }; -static int init_clocksource_sysfs(void) +static int __init init_clocksource_sysfs(void) { int error = sysdev_class_register(&clocksource_sysclass); diff --git a/kernel/timer.c b/kernel/timer.c index eb97371b87d8fab6cef055b873a3ca4333c8656c..524c7f638365541d4e477706669dec4698ab6e62 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -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 __init 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 __init 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); }