[PATCH] Time: Introduce arch generic time accessors
Introduces clocksource switching code and the arch generic time accessor functions that use the clocksource infrastructure. Signed-off-by: John Stultz <johnstul@us.ibm.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
5eb6d20533
commit
cf3c769b4b
3 changed files with 187 additions and 0 deletions
|
@ -102,6 +102,7 @@ extern int do_getitimer(int which, struct itimerval *value);
|
||||||
extern void getnstimeofday(struct timespec *tv);
|
extern void getnstimeofday(struct timespec *tv);
|
||||||
|
|
||||||
extern struct timespec timespec_trunc(struct timespec t, unsigned gran);
|
extern struct timespec timespec_trunc(struct timespec t, unsigned gran);
|
||||||
|
extern int timekeeping_is_continuous(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* timespec_to_ns - Convert timespec to nanoseconds
|
* timespec_to_ns - Convert timespec to nanoseconds
|
||||||
|
@ -144,6 +145,20 @@ extern struct timespec ns_to_timespec(const s64 nsec);
|
||||||
*/
|
*/
|
||||||
extern struct timeval ns_to_timeval(const s64 nsec);
|
extern struct timeval ns_to_timeval(const s64 nsec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* timespec_add_ns - Adds nanoseconds to a timespec
|
||||||
|
* @a: pointer to timespec to be incremented
|
||||||
|
* @ns: unsigned nanoseconds value to be added
|
||||||
|
*/
|
||||||
|
static inline void timespec_add_ns(struct timespec *a, u64 ns)
|
||||||
|
{
|
||||||
|
ns += a->tv_nsec;
|
||||||
|
while(unlikely(ns >= NSEC_PER_SEC)) {
|
||||||
|
ns -= NSEC_PER_SEC;
|
||||||
|
a->tv_sec++;
|
||||||
|
}
|
||||||
|
a->tv_nsec = ns;
|
||||||
|
}
|
||||||
#endif /* __KERNEL__ */
|
#endif /* __KERNEL__ */
|
||||||
|
|
||||||
#define NFDBITS __NFDBITS
|
#define NFDBITS __NFDBITS
|
||||||
|
|
|
@ -523,6 +523,7 @@ EXPORT_SYMBOL(do_gettimeofday);
|
||||||
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
#ifndef CONFIG_GENERIC_TIME
|
||||||
/*
|
/*
|
||||||
* Simulate gettimeofday using do_gettimeofday which only allows a timeval
|
* Simulate gettimeofday using do_gettimeofday which only allows a timeval
|
||||||
* and therefore only yields usec accuracy
|
* and therefore only yields usec accuracy
|
||||||
|
@ -537,6 +538,7 @@ void getnstimeofday(struct timespec *tv)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(getnstimeofday);
|
EXPORT_SYMBOL_GPL(getnstimeofday);
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
|
/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
|
||||||
* Assumes input in normal date format, i.e. 1980-12-31 23:59:59
|
* Assumes input in normal date format, i.e. 1980-12-31 23:59:59
|
||||||
|
|
170
kernel/timer.c
170
kernel/timer.c
|
@ -795,6 +795,169 @@ u64 current_tick_length(long shift)
|
||||||
#include <linux/clocksource.h>
|
#include <linux/clocksource.h>
|
||||||
static struct clocksource *clock; /* pointer to current clocksource */
|
static struct clocksource *clock; /* pointer to current clocksource */
|
||||||
static cycle_t last_clock_cycle; /* cycle value at last update_wall_time */
|
static cycle_t last_clock_cycle; /* cycle value at last update_wall_time */
|
||||||
|
|
||||||
|
#ifdef CONFIG_GENERIC_TIME
|
||||||
|
/**
|
||||||
|
* __get_nsec_offset - Returns nanoseconds since last call to periodic_hook
|
||||||
|
*
|
||||||
|
* private function, must hold xtime_lock lock when being
|
||||||
|
* called. Returns the number of nanoseconds since the
|
||||||
|
* last call to update_wall_time() (adjusted by NTP scaling)
|
||||||
|
*/
|
||||||
|
static inline s64 __get_nsec_offset(void)
|
||||||
|
{
|
||||||
|
cycle_t cycle_now, cycle_delta;
|
||||||
|
s64 ns_offset;
|
||||||
|
|
||||||
|
/* read clocksource: */
|
||||||
|
cycle_now = read_clocksource(clock);
|
||||||
|
|
||||||
|
/* calculate the delta since the last update_wall_time: */
|
||||||
|
cycle_delta = (cycle_now - last_clock_cycle) & clock->mask;
|
||||||
|
|
||||||
|
/* convert to nanoseconds: */
|
||||||
|
ns_offset = cyc2ns(clock, cycle_delta);
|
||||||
|
|
||||||
|
return ns_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __get_realtime_clock_ts - Returns the time of day in a timespec
|
||||||
|
* @ts: pointer to the timespec to be set
|
||||||
|
*
|
||||||
|
* Returns the time of day in a timespec. Used by
|
||||||
|
* do_gettimeofday() and get_realtime_clock_ts().
|
||||||
|
*/
|
||||||
|
static inline void __get_realtime_clock_ts(struct timespec *ts)
|
||||||
|
{
|
||||||
|
unsigned long seq;
|
||||||
|
s64 nsecs;
|
||||||
|
|
||||||
|
do {
|
||||||
|
seq = read_seqbegin(&xtime_lock);
|
||||||
|
|
||||||
|
*ts = xtime;
|
||||||
|
nsecs = __get_nsec_offset();
|
||||||
|
|
||||||
|
} while (read_seqretry(&xtime_lock, seq));
|
||||||
|
|
||||||
|
timespec_add_ns(ts, nsecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get_realtime_clock_ts - Returns the time of day in a timespec
|
||||||
|
* @ts: pointer to the timespec to be set
|
||||||
|
*
|
||||||
|
* Returns the time of day in a timespec.
|
||||||
|
*/
|
||||||
|
void getnstimeofday(struct timespec *ts)
|
||||||
|
{
|
||||||
|
__get_realtime_clock_ts(ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT_SYMBOL(getnstimeofday);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* do_gettimeofday - Returns the time of day in a timeval
|
||||||
|
* @tv: pointer to the timeval to be set
|
||||||
|
*
|
||||||
|
* NOTE: Users should be converted to using get_realtime_clock_ts()
|
||||||
|
*/
|
||||||
|
void do_gettimeofday(struct timeval *tv)
|
||||||
|
{
|
||||||
|
struct timespec now;
|
||||||
|
|
||||||
|
__get_realtime_clock_ts(&now);
|
||||||
|
tv->tv_sec = now.tv_sec;
|
||||||
|
tv->tv_usec = now.tv_nsec/1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT_SYMBOL(do_gettimeofday);
|
||||||
|
/**
|
||||||
|
* do_settimeofday - Sets the time of day
|
||||||
|
* @tv: pointer to the timespec variable containing the new time
|
||||||
|
*
|
||||||
|
* Sets the time of day to the new time and update NTP and notify hrtimers
|
||||||
|
*/
|
||||||
|
int do_settimeofday(struct timespec *tv)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
time_t wtm_sec, sec = tv->tv_sec;
|
||||||
|
long wtm_nsec, nsec = tv->tv_nsec;
|
||||||
|
|
||||||
|
if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
write_seqlock_irqsave(&xtime_lock, flags);
|
||||||
|
|
||||||
|
nsec -= __get_nsec_offset();
|
||||||
|
|
||||||
|
wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
|
||||||
|
wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
|
||||||
|
|
||||||
|
set_normalized_timespec(&xtime, sec, nsec);
|
||||||
|
set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
|
||||||
|
|
||||||
|
ntp_clear();
|
||||||
|
|
||||||
|
write_sequnlock_irqrestore(&xtime_lock, flags);
|
||||||
|
|
||||||
|
/* signal hrtimers about time change */
|
||||||
|
clock_was_set();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT_SYMBOL(do_settimeofday);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* change_clocksource - Swaps clocksources if a new one is available
|
||||||
|
*
|
||||||
|
* Accumulates current time interval and initializes new clocksource
|
||||||
|
*/
|
||||||
|
static int change_clocksource(void)
|
||||||
|
{
|
||||||
|
struct clocksource *new;
|
||||||
|
cycle_t now;
|
||||||
|
u64 nsec;
|
||||||
|
new = get_next_clocksource();
|
||||||
|
if (clock != new) {
|
||||||
|
now = read_clocksource(new);
|
||||||
|
nsec = __get_nsec_offset();
|
||||||
|
timespec_add_ns(&xtime, nsec);
|
||||||
|
|
||||||
|
clock = new;
|
||||||
|
last_clock_cycle = now;
|
||||||
|
printk(KERN_INFO "Time: %s clocksource has been installed.\n",
|
||||||
|
clock->name);
|
||||||
|
return 1;
|
||||||
|
} else if (clock->update_callback) {
|
||||||
|
return clock->update_callback();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define change_clocksource() (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* timeofday_is_continuous - check to see if timekeeping is free running
|
||||||
|
*/
|
||||||
|
int timekeeping_is_continuous(void)
|
||||||
|
{
|
||||||
|
unsigned long seq;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
do {
|
||||||
|
seq = read_seqbegin(&xtime_lock);
|
||||||
|
|
||||||
|
ret = clock->is_continuous;
|
||||||
|
|
||||||
|
} while (read_seqretry(&xtime_lock, seq));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* timekeeping_init - Initializes the clocksource and common timekeeping values
|
* timekeeping_init - Initializes the clocksource and common timekeeping values
|
||||||
*/
|
*/
|
||||||
|
@ -901,6 +1064,13 @@ static void update_wall_time(void)
|
||||||
/* store full nanoseconds into xtime */
|
/* store full nanoseconds into xtime */
|
||||||
xtime.tv_nsec = remainder_snsecs >> clock->shift;
|
xtime.tv_nsec = remainder_snsecs >> clock->shift;
|
||||||
remainder_snsecs -= (s64)xtime.tv_nsec << clock->shift;
|
remainder_snsecs -= (s64)xtime.tv_nsec << clock->shift;
|
||||||
|
|
||||||
|
/* check to see if there is a new clocksource to use */
|
||||||
|
if (change_clocksource()) {
|
||||||
|
error = 0;
|
||||||
|
remainder_snsecs = 0;
|
||||||
|
calculate_clocksource_interval(clock, tick_nsec);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in a new issue