mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 02:32:09 -04:00
Merge branch 'timers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'timers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (34 commits) time: Prevent 32 bit overflow with set_normalized_timespec() clocksource: Delay clocksource down rating to late boot clocksource: clocksource_select must be called with mutex locked clocksource: Resolve cpu hotplug dead lock with TSC unstable, fix crash timers: Drop a function prototype clocksource: Resolve cpu hotplug dead lock with TSC unstable timer.c: Fix S/390 comments timekeeping: Fix invalid getboottime() value timekeeping: Fix up read_persistent_clock() breakage on sh timekeeping: Increase granularity of read_persistent_clock(), build fix time: Introduce CLOCK_REALTIME_COARSE x86: Do not unregister PIT clocksource on PIT oneshot setup/shutdown clocksource: Avoid clocksource watchdog circular locking dependency clocksource: Protect the watchdog rating changes with clocksource_mutex clocksource: Call clocksource_change_rating() outside of watchdog_lock timekeeping: Introduce read_boot_clock timekeeping: Increase granularity of read_persistent_clock() timekeeping: Update clocksource with stop_machine timekeeping: Add timekeeper read_clock helper functions timekeeping: Move NTP adjusted clock multiplier to struct timekeeper ... Fix trivial conflict due to MIPS lemote -> loongson renaming.
This commit is contained in:
commit
a03fdb7612
32 changed files with 917 additions and 640 deletions
|
|
@ -21,7 +21,6 @@
|
|||
*
|
||||
* TODO WishList:
|
||||
* o Allow clocksource drivers to be unregistered
|
||||
* o get rid of clocksource_jiffies extern
|
||||
*/
|
||||
|
||||
#include <linux/clocksource.h>
|
||||
|
|
@ -30,6 +29,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/sched.h> /* for spin_unlock_irq() using preempt_count() m68k */
|
||||
#include <linux/tick.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
void timecounter_init(struct timecounter *tc,
|
||||
const struct cyclecounter *cc,
|
||||
|
|
@ -107,50 +107,35 @@ u64 timecounter_cyc2time(struct timecounter *tc,
|
|||
}
|
||||
EXPORT_SYMBOL(timecounter_cyc2time);
|
||||
|
||||
/* XXX - Would like a better way for initializing curr_clocksource */
|
||||
extern struct clocksource clocksource_jiffies;
|
||||
|
||||
/*[Clocksource internal variables]---------
|
||||
* curr_clocksource:
|
||||
* currently selected clocksource. Initialized to clocksource_jiffies.
|
||||
* next_clocksource:
|
||||
* pending next selected clocksource.
|
||||
* currently selected clocksource.
|
||||
* clocksource_list:
|
||||
* linked list with the registered clocksources
|
||||
* clocksource_lock:
|
||||
* protects manipulations to curr_clocksource and next_clocksource
|
||||
* and the clocksource_list
|
||||
* clocksource_mutex:
|
||||
* protects manipulations to curr_clocksource and the clocksource_list
|
||||
* override_name:
|
||||
* Name of the user-specified clocksource.
|
||||
*/
|
||||
static struct clocksource *curr_clocksource = &clocksource_jiffies;
|
||||
static struct clocksource *next_clocksource;
|
||||
static struct clocksource *clocksource_override;
|
||||
static struct clocksource *curr_clocksource;
|
||||
static LIST_HEAD(clocksource_list);
|
||||
static DEFINE_SPINLOCK(clocksource_lock);
|
||||
static DEFINE_MUTEX(clocksource_mutex);
|
||||
static char override_name[32];
|
||||
static int finished_booting;
|
||||
|
||||
/* clocksource_done_booting - Called near the end of core bootup
|
||||
*
|
||||
* Hack to avoid lots of clocksource churn at boot time.
|
||||
* We use fs_initcall because we want this to start before
|
||||
* device_initcall but after subsys_initcall.
|
||||
*/
|
||||
static int __init clocksource_done_booting(void)
|
||||
{
|
||||
finished_booting = 1;
|
||||
return 0;
|
||||
}
|
||||
fs_initcall(clocksource_done_booting);
|
||||
|
||||
#ifdef CONFIG_CLOCKSOURCE_WATCHDOG
|
||||
static void clocksource_watchdog_work(struct work_struct *work);
|
||||
|
||||
static LIST_HEAD(watchdog_list);
|
||||
static struct clocksource *watchdog;
|
||||
static struct timer_list watchdog_timer;
|
||||
static DECLARE_WORK(watchdog_work, clocksource_watchdog_work);
|
||||
static DEFINE_SPINLOCK(watchdog_lock);
|
||||
static cycle_t watchdog_last;
|
||||
static unsigned long watchdog_resumed;
|
||||
static int watchdog_running;
|
||||
|
||||
static int clocksource_watchdog_kthread(void *data);
|
||||
static void __clocksource_change_rating(struct clocksource *cs, int rating);
|
||||
|
||||
/*
|
||||
* Interval: 0.5sec Threshold: 0.0625s
|
||||
|
|
@ -158,135 +143,249 @@ static unsigned long watchdog_resumed;
|
|||
#define WATCHDOG_INTERVAL (HZ >> 1)
|
||||
#define WATCHDOG_THRESHOLD (NSEC_PER_SEC >> 4)
|
||||
|
||||
static void clocksource_ratewd(struct clocksource *cs, int64_t delta)
|
||||
static void clocksource_watchdog_work(struct work_struct *work)
|
||||
{
|
||||
if (delta > -WATCHDOG_THRESHOLD && delta < WATCHDOG_THRESHOLD)
|
||||
return;
|
||||
/*
|
||||
* If kthread_run fails the next watchdog scan over the
|
||||
* watchdog_list will find the unstable clock again.
|
||||
*/
|
||||
kthread_run(clocksource_watchdog_kthread, NULL, "kwatchdog");
|
||||
}
|
||||
|
||||
static void __clocksource_unstable(struct clocksource *cs)
|
||||
{
|
||||
cs->flags &= ~(CLOCK_SOURCE_VALID_FOR_HRES | CLOCK_SOURCE_WATCHDOG);
|
||||
cs->flags |= CLOCK_SOURCE_UNSTABLE;
|
||||
if (finished_booting)
|
||||
schedule_work(&watchdog_work);
|
||||
}
|
||||
|
||||
static void clocksource_unstable(struct clocksource *cs, int64_t delta)
|
||||
{
|
||||
printk(KERN_WARNING "Clocksource %s unstable (delta = %Ld ns)\n",
|
||||
cs->name, delta);
|
||||
cs->flags &= ~(CLOCK_SOURCE_VALID_FOR_HRES | CLOCK_SOURCE_WATCHDOG);
|
||||
clocksource_change_rating(cs, 0);
|
||||
list_del(&cs->wd_list);
|
||||
__clocksource_unstable(cs);
|
||||
}
|
||||
|
||||
/**
|
||||
* clocksource_mark_unstable - mark clocksource unstable via watchdog
|
||||
* @cs: clocksource to be marked unstable
|
||||
*
|
||||
* This function is called instead of clocksource_change_rating from
|
||||
* cpu hotplug code to avoid a deadlock between the clocksource mutex
|
||||
* and the cpu hotplug mutex. It defers the update of the clocksource
|
||||
* to the watchdog thread.
|
||||
*/
|
||||
void clocksource_mark_unstable(struct clocksource *cs)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&watchdog_lock, flags);
|
||||
if (!(cs->flags & CLOCK_SOURCE_UNSTABLE)) {
|
||||
if (list_empty(&cs->wd_list))
|
||||
list_add(&cs->wd_list, &watchdog_list);
|
||||
__clocksource_unstable(cs);
|
||||
}
|
||||
spin_unlock_irqrestore(&watchdog_lock, flags);
|
||||
}
|
||||
|
||||
static void clocksource_watchdog(unsigned long data)
|
||||
{
|
||||
struct clocksource *cs, *tmp;
|
||||
struct clocksource *cs;
|
||||
cycle_t csnow, wdnow;
|
||||
int64_t wd_nsec, cs_nsec;
|
||||
int resumed;
|
||||
int next_cpu;
|
||||
|
||||
spin_lock(&watchdog_lock);
|
||||
|
||||
resumed = test_and_clear_bit(0, &watchdog_resumed);
|
||||
if (!watchdog_running)
|
||||
goto out;
|
||||
|
||||
wdnow = watchdog->read(watchdog);
|
||||
wd_nsec = cyc2ns(watchdog, (wdnow - watchdog_last) & watchdog->mask);
|
||||
wd_nsec = clocksource_cyc2ns((wdnow - watchdog_last) & watchdog->mask,
|
||||
watchdog->mult, watchdog->shift);
|
||||
watchdog_last = wdnow;
|
||||
|
||||
list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list) {
|
||||
list_for_each_entry(cs, &watchdog_list, wd_list) {
|
||||
|
||||
/* Clocksource already marked unstable? */
|
||||
if (cs->flags & CLOCK_SOURCE_UNSTABLE) {
|
||||
if (finished_booting)
|
||||
schedule_work(&watchdog_work);
|
||||
continue;
|
||||
}
|
||||
|
||||
csnow = cs->read(cs);
|
||||
|
||||
if (unlikely(resumed)) {
|
||||
/* Clocksource initialized ? */
|
||||
if (!(cs->flags & CLOCK_SOURCE_WATCHDOG)) {
|
||||
cs->flags |= CLOCK_SOURCE_WATCHDOG;
|
||||
cs->wd_last = csnow;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Initialized ? */
|
||||
if (!(cs->flags & CLOCK_SOURCE_WATCHDOG)) {
|
||||
if ((cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) &&
|
||||
(watchdog->flags & CLOCK_SOURCE_IS_CONTINUOUS)) {
|
||||
cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES;
|
||||
/*
|
||||
* We just marked the clocksource as
|
||||
* highres-capable, notify the rest of the
|
||||
* system as well so that we transition
|
||||
* into high-res mode:
|
||||
*/
|
||||
tick_clock_notify();
|
||||
}
|
||||
cs->flags |= CLOCK_SOURCE_WATCHDOG;
|
||||
cs->wd_last = csnow;
|
||||
} else {
|
||||
cs_nsec = cyc2ns(cs, (csnow - cs->wd_last) & cs->mask);
|
||||
cs->wd_last = csnow;
|
||||
/* Check the delta. Might remove from the list ! */
|
||||
clocksource_ratewd(cs, cs_nsec - wd_nsec);
|
||||
/* Check the deviation from the watchdog clocksource. */
|
||||
cs_nsec = clocksource_cyc2ns((csnow - cs->wd_last) &
|
||||
cs->mask, cs->mult, cs->shift);
|
||||
cs->wd_last = csnow;
|
||||
if (abs(cs_nsec - wd_nsec) > WATCHDOG_THRESHOLD) {
|
||||
clocksource_unstable(cs, cs_nsec - wd_nsec);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) &&
|
||||
(cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) &&
|
||||
(watchdog->flags & CLOCK_SOURCE_IS_CONTINUOUS)) {
|
||||
cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES;
|
||||
/*
|
||||
* We just marked the clocksource as highres-capable,
|
||||
* notify the rest of the system as well so that we
|
||||
* transition into high-res mode:
|
||||
*/
|
||||
tick_clock_notify();
|
||||
}
|
||||
}
|
||||
|
||||
if (!list_empty(&watchdog_list)) {
|
||||
/*
|
||||
* Cycle through CPUs to check if the CPUs stay
|
||||
* synchronized to each other.
|
||||
*/
|
||||
int next_cpu = cpumask_next(raw_smp_processor_id(),
|
||||
cpu_online_mask);
|
||||
|
||||
if (next_cpu >= nr_cpu_ids)
|
||||
next_cpu = cpumask_first(cpu_online_mask);
|
||||
watchdog_timer.expires += WATCHDOG_INTERVAL;
|
||||
add_timer_on(&watchdog_timer, next_cpu);
|
||||
}
|
||||
/*
|
||||
* Cycle through CPUs to check if the CPUs stay synchronized
|
||||
* to each other.
|
||||
*/
|
||||
next_cpu = cpumask_next(raw_smp_processor_id(), cpu_online_mask);
|
||||
if (next_cpu >= nr_cpu_ids)
|
||||
next_cpu = cpumask_first(cpu_online_mask);
|
||||
watchdog_timer.expires += WATCHDOG_INTERVAL;
|
||||
add_timer_on(&watchdog_timer, next_cpu);
|
||||
out:
|
||||
spin_unlock(&watchdog_lock);
|
||||
}
|
||||
static void clocksource_resume_watchdog(void)
|
||||
|
||||
static inline void clocksource_start_watchdog(void)
|
||||
{
|
||||
set_bit(0, &watchdog_resumed);
|
||||
if (watchdog_running || !watchdog || list_empty(&watchdog_list))
|
||||
return;
|
||||
init_timer(&watchdog_timer);
|
||||
watchdog_timer.function = clocksource_watchdog;
|
||||
watchdog_last = watchdog->read(watchdog);
|
||||
watchdog_timer.expires = jiffies + WATCHDOG_INTERVAL;
|
||||
add_timer_on(&watchdog_timer, cpumask_first(cpu_online_mask));
|
||||
watchdog_running = 1;
|
||||
}
|
||||
|
||||
static void clocksource_check_watchdog(struct clocksource *cs)
|
||||
static inline void clocksource_stop_watchdog(void)
|
||||
{
|
||||
if (!watchdog_running || (watchdog && !list_empty(&watchdog_list)))
|
||||
return;
|
||||
del_timer(&watchdog_timer);
|
||||
watchdog_running = 0;
|
||||
}
|
||||
|
||||
static inline void clocksource_reset_watchdog(void)
|
||||
{
|
||||
struct clocksource *cs;
|
||||
|
||||
list_for_each_entry(cs, &watchdog_list, wd_list)
|
||||
cs->flags &= ~CLOCK_SOURCE_WATCHDOG;
|
||||
}
|
||||
|
||||
static void clocksource_resume_watchdog(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&watchdog_lock, flags);
|
||||
clocksource_reset_watchdog();
|
||||
spin_unlock_irqrestore(&watchdog_lock, flags);
|
||||
}
|
||||
|
||||
static void clocksource_enqueue_watchdog(struct clocksource *cs)
|
||||
{
|
||||
struct clocksource *cse;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&watchdog_lock, flags);
|
||||
if (cs->flags & CLOCK_SOURCE_MUST_VERIFY) {
|
||||
int started = !list_empty(&watchdog_list);
|
||||
|
||||
/* cs is a clocksource to be watched. */
|
||||
list_add(&cs->wd_list, &watchdog_list);
|
||||
if (!started && watchdog) {
|
||||
watchdog_last = watchdog->read(watchdog);
|
||||
watchdog_timer.expires = jiffies + WATCHDOG_INTERVAL;
|
||||
add_timer_on(&watchdog_timer,
|
||||
cpumask_first(cpu_online_mask));
|
||||
}
|
||||
cs->flags &= ~CLOCK_SOURCE_WATCHDOG;
|
||||
} else {
|
||||
/* cs is a watchdog. */
|
||||
if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS)
|
||||
cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES;
|
||||
|
||||
/* Pick the best watchdog. */
|
||||
if (!watchdog || cs->rating > watchdog->rating) {
|
||||
if (watchdog)
|
||||
del_timer(&watchdog_timer);
|
||||
watchdog = cs;
|
||||
init_timer(&watchdog_timer);
|
||||
watchdog_timer.function = clocksource_watchdog;
|
||||
|
||||
/* Reset watchdog cycles */
|
||||
list_for_each_entry(cse, &watchdog_list, wd_list)
|
||||
cse->flags &= ~CLOCK_SOURCE_WATCHDOG;
|
||||
/* Start if list is not empty */
|
||||
if (!list_empty(&watchdog_list)) {
|
||||
watchdog_last = watchdog->read(watchdog);
|
||||
watchdog_timer.expires =
|
||||
jiffies + WATCHDOG_INTERVAL;
|
||||
add_timer_on(&watchdog_timer,
|
||||
cpumask_first(cpu_online_mask));
|
||||
}
|
||||
clocksource_reset_watchdog();
|
||||
}
|
||||
}
|
||||
/* Check if the watchdog timer needs to be started. */
|
||||
clocksource_start_watchdog();
|
||||
spin_unlock_irqrestore(&watchdog_lock, flags);
|
||||
}
|
||||
#else
|
||||
static void clocksource_check_watchdog(struct clocksource *cs)
|
||||
|
||||
static void clocksource_dequeue_watchdog(struct clocksource *cs)
|
||||
{
|
||||
struct clocksource *tmp;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&watchdog_lock, flags);
|
||||
if (cs->flags & CLOCK_SOURCE_MUST_VERIFY) {
|
||||
/* cs is a watched clocksource. */
|
||||
list_del_init(&cs->wd_list);
|
||||
} else if (cs == watchdog) {
|
||||
/* Reset watchdog cycles */
|
||||
clocksource_reset_watchdog();
|
||||
/* Current watchdog is removed. Find an alternative. */
|
||||
watchdog = NULL;
|
||||
list_for_each_entry(tmp, &clocksource_list, list) {
|
||||
if (tmp == cs || tmp->flags & CLOCK_SOURCE_MUST_VERIFY)
|
||||
continue;
|
||||
if (!watchdog || tmp->rating > watchdog->rating)
|
||||
watchdog = tmp;
|
||||
}
|
||||
}
|
||||
cs->flags &= ~CLOCK_SOURCE_WATCHDOG;
|
||||
/* Check if the watchdog timer needs to be stopped. */
|
||||
clocksource_stop_watchdog();
|
||||
spin_unlock_irqrestore(&watchdog_lock, flags);
|
||||
}
|
||||
|
||||
static int clocksource_watchdog_kthread(void *data)
|
||||
{
|
||||
struct clocksource *cs, *tmp;
|
||||
unsigned long flags;
|
||||
LIST_HEAD(unstable);
|
||||
|
||||
mutex_lock(&clocksource_mutex);
|
||||
spin_lock_irqsave(&watchdog_lock, flags);
|
||||
list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list)
|
||||
if (cs->flags & CLOCK_SOURCE_UNSTABLE) {
|
||||
list_del_init(&cs->wd_list);
|
||||
list_add(&cs->wd_list, &unstable);
|
||||
}
|
||||
/* Check if the watchdog timer needs to be stopped. */
|
||||
clocksource_stop_watchdog();
|
||||
spin_unlock_irqrestore(&watchdog_lock, flags);
|
||||
|
||||
/* Needs to be done outside of watchdog lock */
|
||||
list_for_each_entry_safe(cs, tmp, &unstable, wd_list) {
|
||||
list_del_init(&cs->wd_list);
|
||||
__clocksource_change_rating(cs, 0);
|
||||
}
|
||||
mutex_unlock(&clocksource_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* CONFIG_CLOCKSOURCE_WATCHDOG */
|
||||
|
||||
static void clocksource_enqueue_watchdog(struct clocksource *cs)
|
||||
{
|
||||
if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS)
|
||||
cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES;
|
||||
}
|
||||
|
||||
static inline void clocksource_dequeue_watchdog(struct clocksource *cs) { }
|
||||
static inline void clocksource_resume_watchdog(void) { }
|
||||
#endif
|
||||
static inline int clocksource_watchdog_kthread(void *data) { return 0; }
|
||||
|
||||
#endif /* CONFIG_CLOCKSOURCE_WATCHDOG */
|
||||
|
||||
/**
|
||||
* clocksource_resume - resume the clocksource(s)
|
||||
|
|
@ -294,18 +393,16 @@ static inline void clocksource_resume_watchdog(void) { }
|
|||
void clocksource_resume(void)
|
||||
{
|
||||
struct clocksource *cs;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&clocksource_lock, flags);
|
||||
mutex_lock(&clocksource_mutex);
|
||||
|
||||
list_for_each_entry(cs, &clocksource_list, list) {
|
||||
list_for_each_entry(cs, &clocksource_list, list)
|
||||
if (cs->resume)
|
||||
cs->resume();
|
||||
}
|
||||
|
||||
clocksource_resume_watchdog();
|
||||
|
||||
spin_unlock_irqrestore(&clocksource_lock, flags);
|
||||
mutex_unlock(&clocksource_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -320,75 +417,94 @@ void clocksource_touch_watchdog(void)
|
|||
clocksource_resume_watchdog();
|
||||
}
|
||||
|
||||
/**
|
||||
* clocksource_get_next - Returns the selected clocksource
|
||||
*
|
||||
*/
|
||||
struct clocksource *clocksource_get_next(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&clocksource_lock, flags);
|
||||
if (next_clocksource && finished_booting) {
|
||||
curr_clocksource = next_clocksource;
|
||||
next_clocksource = NULL;
|
||||
}
|
||||
spin_unlock_irqrestore(&clocksource_lock, flags);
|
||||
|
||||
return curr_clocksource;
|
||||
}
|
||||
#ifdef CONFIG_GENERIC_TIME
|
||||
|
||||
/**
|
||||
* select_clocksource - Selects the best registered clocksource.
|
||||
* clocksource_select - Select the best clocksource available
|
||||
*
|
||||
* Private function. Must hold clocksource_lock when called.
|
||||
* Private function. Must hold clocksource_mutex when called.
|
||||
*
|
||||
* Select the clocksource with the best rating, or the clocksource,
|
||||
* which is selected by userspace override.
|
||||
*/
|
||||
static struct clocksource *select_clocksource(void)
|
||||
static void clocksource_select(void)
|
||||
{
|
||||
struct clocksource *next;
|
||||
struct clocksource *best, *cs;
|
||||
|
||||
if (list_empty(&clocksource_list))
|
||||
return NULL;
|
||||
|
||||
if (clocksource_override)
|
||||
next = clocksource_override;
|
||||
else
|
||||
next = list_entry(clocksource_list.next, struct clocksource,
|
||||
list);
|
||||
|
||||
if (next == curr_clocksource)
|
||||
return NULL;
|
||||
|
||||
return next;
|
||||
if (!finished_booting || list_empty(&clocksource_list))
|
||||
return;
|
||||
/* First clocksource on the list has the best rating. */
|
||||
best = list_first_entry(&clocksource_list, struct clocksource, list);
|
||||
/* Check for the override clocksource. */
|
||||
list_for_each_entry(cs, &clocksource_list, list) {
|
||||
if (strcmp(cs->name, override_name) != 0)
|
||||
continue;
|
||||
/*
|
||||
* Check to make sure we don't switch to a non-highres
|
||||
* capable clocksource if the tick code is in oneshot
|
||||
* mode (highres or nohz)
|
||||
*/
|
||||
if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) &&
|
||||
tick_oneshot_mode_active()) {
|
||||
/* Override clocksource cannot be used. */
|
||||
printk(KERN_WARNING "Override clocksource %s is not "
|
||||
"HRT compatible. Cannot switch while in "
|
||||
"HRT/NOHZ mode\n", cs->name);
|
||||
override_name[0] = 0;
|
||||
} else
|
||||
/* Override clocksource can be used. */
|
||||
best = cs;
|
||||
break;
|
||||
}
|
||||
if (curr_clocksource != best) {
|
||||
printk(KERN_INFO "Switching to clocksource %s\n", best->name);
|
||||
curr_clocksource = best;
|
||||
timekeeping_notify(curr_clocksource);
|
||||
}
|
||||
}
|
||||
|
||||
#else /* CONFIG_GENERIC_TIME */
|
||||
|
||||
static inline void clocksource_select(void) { }
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* clocksource_done_booting - Called near the end of core bootup
|
||||
*
|
||||
* Hack to avoid lots of clocksource churn at boot time.
|
||||
* We use fs_initcall because we want this to start before
|
||||
* device_initcall but after subsys_initcall.
|
||||
*/
|
||||
static int __init clocksource_done_booting(void)
|
||||
{
|
||||
finished_booting = 1;
|
||||
|
||||
/*
|
||||
* Run the watchdog first to eliminate unstable clock sources
|
||||
*/
|
||||
clocksource_watchdog_kthread(NULL);
|
||||
|
||||
mutex_lock(&clocksource_mutex);
|
||||
clocksource_select();
|
||||
mutex_unlock(&clocksource_mutex);
|
||||
return 0;
|
||||
}
|
||||
fs_initcall(clocksource_done_booting);
|
||||
|
||||
/*
|
||||
* Enqueue the clocksource sorted by rating
|
||||
*/
|
||||
static int clocksource_enqueue(struct clocksource *c)
|
||||
static void clocksource_enqueue(struct clocksource *cs)
|
||||
{
|
||||
struct list_head *tmp, *entry = &clocksource_list;
|
||||
struct list_head *entry = &clocksource_list;
|
||||
struct clocksource *tmp;
|
||||
|
||||
list_for_each(tmp, &clocksource_list) {
|
||||
struct clocksource *cs;
|
||||
|
||||
cs = list_entry(tmp, struct clocksource, list);
|
||||
if (cs == c)
|
||||
return -EBUSY;
|
||||
list_for_each_entry(tmp, &clocksource_list, list)
|
||||
/* Keep track of the place, where to insert */
|
||||
if (cs->rating >= c->rating)
|
||||
entry = tmp;
|
||||
}
|
||||
list_add(&c->list, entry);
|
||||
|
||||
if (strlen(c->name) == strlen(override_name) &&
|
||||
!strcmp(c->name, override_name))
|
||||
clocksource_override = c;
|
||||
|
||||
return 0;
|
||||
if (tmp->rating >= cs->rating)
|
||||
entry = &tmp->list;
|
||||
list_add(&cs->list, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -397,52 +513,48 @@ static int clocksource_enqueue(struct clocksource *c)
|
|||
*
|
||||
* Returns -EBUSY if registration fails, zero otherwise.
|
||||
*/
|
||||
int clocksource_register(struct clocksource *c)
|
||||
int clocksource_register(struct clocksource *cs)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&clocksource_lock, flags);
|
||||
ret = clocksource_enqueue(c);
|
||||
if (!ret)
|
||||
next_clocksource = select_clocksource();
|
||||
spin_unlock_irqrestore(&clocksource_lock, flags);
|
||||
if (!ret)
|
||||
clocksource_check_watchdog(c);
|
||||
return ret;
|
||||
mutex_lock(&clocksource_mutex);
|
||||
clocksource_enqueue(cs);
|
||||
clocksource_select();
|
||||
clocksource_enqueue_watchdog(cs);
|
||||
mutex_unlock(&clocksource_mutex);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(clocksource_register);
|
||||
|
||||
/**
|
||||
* clocksource_change_rating - Change the rating of a registered clocksource
|
||||
*
|
||||
*/
|
||||
void clocksource_change_rating(struct clocksource *cs, int rating)
|
||||
static void __clocksource_change_rating(struct clocksource *cs, int rating)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&clocksource_lock, flags);
|
||||
list_del(&cs->list);
|
||||
cs->rating = rating;
|
||||
clocksource_enqueue(cs);
|
||||
next_clocksource = select_clocksource();
|
||||
spin_unlock_irqrestore(&clocksource_lock, flags);
|
||||
clocksource_select();
|
||||
}
|
||||
|
||||
/**
|
||||
* clocksource_change_rating - Change the rating of a registered clocksource
|
||||
*/
|
||||
void clocksource_change_rating(struct clocksource *cs, int rating)
|
||||
{
|
||||
mutex_lock(&clocksource_mutex);
|
||||
__clocksource_change_rating(cs, rating);
|
||||
mutex_unlock(&clocksource_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(clocksource_change_rating);
|
||||
|
||||
/**
|
||||
* clocksource_unregister - remove a registered clocksource
|
||||
*/
|
||||
void clocksource_unregister(struct clocksource *cs)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&clocksource_lock, flags);
|
||||
mutex_lock(&clocksource_mutex);
|
||||
clocksource_dequeue_watchdog(cs);
|
||||
list_del(&cs->list);
|
||||
if (clocksource_override == cs)
|
||||
clocksource_override = NULL;
|
||||
next_clocksource = select_clocksource();
|
||||
spin_unlock_irqrestore(&clocksource_lock, flags);
|
||||
clocksource_select();
|
||||
mutex_unlock(&clocksource_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(clocksource_unregister);
|
||||
|
||||
#ifdef CONFIG_SYSFS
|
||||
/**
|
||||
|
|
@ -458,9 +570,9 @@ sysfs_show_current_clocksources(struct sys_device *dev,
|
|||
{
|
||||
ssize_t count = 0;
|
||||
|
||||
spin_lock_irq(&clocksource_lock);
|
||||
mutex_lock(&clocksource_mutex);
|
||||
count = snprintf(buf, PAGE_SIZE, "%s\n", curr_clocksource->name);
|
||||
spin_unlock_irq(&clocksource_lock);
|
||||
mutex_unlock(&clocksource_mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
|
@ -478,9 +590,7 @@ static ssize_t sysfs_override_clocksource(struct sys_device *dev,
|
|||
struct sysdev_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct clocksource *ovr = NULL;
|
||||
size_t ret = count;
|
||||
int len;
|
||||
|
||||
/* strings from sysfs write are not 0 terminated! */
|
||||
if (count >= sizeof(override_name))
|
||||
|
|
@ -490,44 +600,14 @@ static ssize_t sysfs_override_clocksource(struct sys_device *dev,
|
|||
if (buf[count-1] == '\n')
|
||||
count--;
|
||||
|
||||
spin_lock_irq(&clocksource_lock);
|
||||
mutex_lock(&clocksource_mutex);
|
||||
|
||||
if (count > 0)
|
||||
memcpy(override_name, buf, count);
|
||||
override_name[count] = 0;
|
||||
clocksource_select();
|
||||
|
||||
len = strlen(override_name);
|
||||
if (len) {
|
||||
struct clocksource *cs;
|
||||
|
||||
ovr = clocksource_override;
|
||||
/* try to select it: */
|
||||
list_for_each_entry(cs, &clocksource_list, list) {
|
||||
if (strlen(cs->name) == len &&
|
||||
!strcmp(cs->name, override_name))
|
||||
ovr = cs;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to make sure we don't switch to a non-highres capable
|
||||
* clocksource if the tick code is in oneshot mode (highres or nohz)
|
||||
*/
|
||||
if (tick_oneshot_mode_active() && ovr &&
|
||||
!(ovr->flags & CLOCK_SOURCE_VALID_FOR_HRES)) {
|
||||
printk(KERN_WARNING "%s clocksource is not HRT compatible. "
|
||||
"Cannot switch while in HRT/NOHZ mode\n", ovr->name);
|
||||
ovr = NULL;
|
||||
override_name[0] = 0;
|
||||
}
|
||||
|
||||
/* Reselect, when the override name has changed */
|
||||
if (ovr != clocksource_override) {
|
||||
clocksource_override = ovr;
|
||||
next_clocksource = select_clocksource();
|
||||
}
|
||||
|
||||
spin_unlock_irq(&clocksource_lock);
|
||||
mutex_unlock(&clocksource_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -547,7 +627,7 @@ sysfs_show_available_clocksources(struct sys_device *dev,
|
|||
struct clocksource *src;
|
||||
ssize_t count = 0;
|
||||
|
||||
spin_lock_irq(&clocksource_lock);
|
||||
mutex_lock(&clocksource_mutex);
|
||||
list_for_each_entry(src, &clocksource_list, list) {
|
||||
/*
|
||||
* Don't show non-HRES clocksource if the tick code is
|
||||
|
|
@ -559,7 +639,7 @@ sysfs_show_available_clocksources(struct sys_device *dev,
|
|||
max((ssize_t)PAGE_SIZE - count, (ssize_t)0),
|
||||
"%s ", src->name);
|
||||
}
|
||||
spin_unlock_irq(&clocksource_lock);
|
||||
mutex_unlock(&clocksource_mutex);
|
||||
|
||||
count += snprintf(buf + count,
|
||||
max((ssize_t)PAGE_SIZE - count, (ssize_t)0), "\n");
|
||||
|
|
@ -614,11 +694,10 @@ device_initcall(init_clocksource_sysfs);
|
|||
*/
|
||||
static int __init boot_override_clocksource(char* str)
|
||||
{
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&clocksource_lock, flags);
|
||||
mutex_lock(&clocksource_mutex);
|
||||
if (str)
|
||||
strlcpy(override_name, str, sizeof(override_name));
|
||||
spin_unlock_irqrestore(&clocksource_lock, flags);
|
||||
mutex_unlock(&clocksource_mutex);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ struct clocksource clocksource_jiffies = {
|
|||
.read = jiffies_read,
|
||||
.mask = 0xffffffff, /*32bits*/
|
||||
.mult = NSEC_PER_JIFFY << JIFFIES_SHIFT, /* details above */
|
||||
.mult_orig = NSEC_PER_JIFFY << JIFFIES_SHIFT,
|
||||
.shift = JIFFIES_SHIFT,
|
||||
};
|
||||
|
||||
|
|
@ -71,3 +70,8 @@ static int __init init_jiffies_clocksource(void)
|
|||
}
|
||||
|
||||
core_initcall(init_jiffies_clocksource);
|
||||
|
||||
struct clocksource * __init __weak clocksource_default_clock(void)
|
||||
{
|
||||
return &clocksource_jiffies;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -194,8 +194,7 @@ static enum hrtimer_restart ntp_leap_second(struct hrtimer *timer)
|
|||
case TIME_OK:
|
||||
break;
|
||||
case TIME_INS:
|
||||
xtime.tv_sec--;
|
||||
wall_to_monotonic.tv_sec++;
|
||||
timekeeping_leap_insert(-1);
|
||||
time_state = TIME_OOP;
|
||||
printk(KERN_NOTICE
|
||||
"Clock: inserting leap second 23:59:60 UTC\n");
|
||||
|
|
@ -203,9 +202,8 @@ static enum hrtimer_restart ntp_leap_second(struct hrtimer *timer)
|
|||
res = HRTIMER_RESTART;
|
||||
break;
|
||||
case TIME_DEL:
|
||||
xtime.tv_sec++;
|
||||
timekeeping_leap_insert(1);
|
||||
time_tai--;
|
||||
wall_to_monotonic.tv_sec--;
|
||||
time_state = TIME_WAIT;
|
||||
printk(KERN_NOTICE
|
||||
"Clock: deleting leap second 23:59:59 UTC\n");
|
||||
|
|
@ -219,7 +217,6 @@ static enum hrtimer_restart ntp_leap_second(struct hrtimer *timer)
|
|||
time_state = TIME_OK;
|
||||
break;
|
||||
}
|
||||
update_vsyscall(&xtime, clock);
|
||||
|
||||
write_sequnlock(&xtime_lock);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,117 @@
|
|||
#include <linux/jiffies.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/tick.h>
|
||||
#include <linux/stop_machine.h>
|
||||
|
||||
/* Structure holding internal timekeeping values. */
|
||||
struct timekeeper {
|
||||
/* Current clocksource used for timekeeping. */
|
||||
struct clocksource *clock;
|
||||
/* The shift value of the current clocksource. */
|
||||
int shift;
|
||||
|
||||
/* Number of clock cycles in one NTP interval. */
|
||||
cycle_t cycle_interval;
|
||||
/* Number of clock shifted nano seconds in one NTP interval. */
|
||||
u64 xtime_interval;
|
||||
/* Raw nano seconds accumulated per NTP interval. */
|
||||
u32 raw_interval;
|
||||
|
||||
/* Clock shifted nano seconds remainder not stored in xtime.tv_nsec. */
|
||||
u64 xtime_nsec;
|
||||
/* Difference between accumulated time and NTP time in ntp
|
||||
* shifted nano seconds. */
|
||||
s64 ntp_error;
|
||||
/* Shift conversion between clock shifted nano seconds and
|
||||
* ntp shifted nano seconds. */
|
||||
int ntp_error_shift;
|
||||
/* NTP adjusted clock multiplier */
|
||||
u32 mult;
|
||||
};
|
||||
|
||||
struct timekeeper timekeeper;
|
||||
|
||||
/**
|
||||
* timekeeper_setup_internals - Set up internals to use clocksource clock.
|
||||
*
|
||||
* @clock: Pointer to clocksource.
|
||||
*
|
||||
* Calculates a fixed cycle/nsec interval for a given clocksource/adjustment
|
||||
* pair and interval request.
|
||||
*
|
||||
* Unless you're the timekeeping code, you should not be using this!
|
||||
*/
|
||||
static void timekeeper_setup_internals(struct clocksource *clock)
|
||||
{
|
||||
cycle_t interval;
|
||||
u64 tmp;
|
||||
|
||||
timekeeper.clock = clock;
|
||||
clock->cycle_last = clock->read(clock);
|
||||
|
||||
/* Do the ns -> cycle conversion first, using original mult */
|
||||
tmp = NTP_INTERVAL_LENGTH;
|
||||
tmp <<= clock->shift;
|
||||
tmp += clock->mult/2;
|
||||
do_div(tmp, clock->mult);
|
||||
if (tmp == 0)
|
||||
tmp = 1;
|
||||
|
||||
interval = (cycle_t) tmp;
|
||||
timekeeper.cycle_interval = interval;
|
||||
|
||||
/* Go back from cycles -> shifted ns */
|
||||
timekeeper.xtime_interval = (u64) interval * clock->mult;
|
||||
timekeeper.raw_interval =
|
||||
((u64) interval * clock->mult) >> clock->shift;
|
||||
|
||||
timekeeper.xtime_nsec = 0;
|
||||
timekeeper.shift = clock->shift;
|
||||
|
||||
timekeeper.ntp_error = 0;
|
||||
timekeeper.ntp_error_shift = NTP_SCALE_SHIFT - clock->shift;
|
||||
|
||||
/*
|
||||
* The timekeeper keeps its own mult values for the currently
|
||||
* active clocksource. These value will be adjusted via NTP
|
||||
* to counteract clock drifting.
|
||||
*/
|
||||
timekeeper.mult = clock->mult;
|
||||
}
|
||||
|
||||
/* Timekeeper helper functions. */
|
||||
static inline s64 timekeeping_get_ns(void)
|
||||
{
|
||||
cycle_t cycle_now, cycle_delta;
|
||||
struct clocksource *clock;
|
||||
|
||||
/* read clocksource: */
|
||||
clock = timekeeper.clock;
|
||||
cycle_now = clock->read(clock);
|
||||
|
||||
/* calculate the delta since the last update_wall_time: */
|
||||
cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;
|
||||
|
||||
/* return delta convert to nanoseconds using ntp adjusted mult. */
|
||||
return clocksource_cyc2ns(cycle_delta, timekeeper.mult,
|
||||
timekeeper.shift);
|
||||
}
|
||||
|
||||
static inline s64 timekeeping_get_ns_raw(void)
|
||||
{
|
||||
cycle_t cycle_now, cycle_delta;
|
||||
struct clocksource *clock;
|
||||
|
||||
/* read clocksource: */
|
||||
clock = timekeeper.clock;
|
||||
cycle_now = clock->read(clock);
|
||||
|
||||
/* calculate the delta since the last update_wall_time: */
|
||||
cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;
|
||||
|
||||
/* return delta convert to nanoseconds using ntp adjusted mult. */
|
||||
return clocksource_cyc2ns(cycle_delta, clock->mult, clock->shift);
|
||||
}
|
||||
|
||||
/*
|
||||
* This read-write spinlock protects us from races in SMP while
|
||||
|
|
@ -44,7 +154,12 @@ __cacheline_aligned_in_smp DEFINE_SEQLOCK(xtime_lock);
|
|||
*/
|
||||
struct timespec xtime __attribute__ ((aligned (16)));
|
||||
struct timespec wall_to_monotonic __attribute__ ((aligned (16)));
|
||||
static unsigned long total_sleep_time; /* seconds */
|
||||
static struct timespec total_sleep_time;
|
||||
|
||||
/*
|
||||
* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock.
|
||||
*/
|
||||
struct timespec raw_time;
|
||||
|
||||
/* flag for if timekeeping is suspended */
|
||||
int __read_mostly timekeeping_suspended;
|
||||
|
|
@ -56,35 +171,44 @@ void update_xtime_cache(u64 nsec)
|
|||
timespec_add_ns(&xtime_cache, nsec);
|
||||
}
|
||||
|
||||
struct clocksource *clock;
|
||||
|
||||
/* must hold xtime_lock */
|
||||
void timekeeping_leap_insert(int leapsecond)
|
||||
{
|
||||
xtime.tv_sec += leapsecond;
|
||||
wall_to_monotonic.tv_sec -= leapsecond;
|
||||
update_vsyscall(&xtime, timekeeper.clock);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GENERIC_TIME
|
||||
|
||||
/**
|
||||
* clocksource_forward_now - update clock to the current time
|
||||
* timekeeping_forward_now - update clock to the current time
|
||||
*
|
||||
* Forward the current clock to update its state since the last call to
|
||||
* update_wall_time(). This is useful before significant clock changes,
|
||||
* as it avoids having to deal with this time offset explicitly.
|
||||
*/
|
||||
static void clocksource_forward_now(void)
|
||||
static void timekeeping_forward_now(void)
|
||||
{
|
||||
cycle_t cycle_now, cycle_delta;
|
||||
struct clocksource *clock;
|
||||
s64 nsec;
|
||||
|
||||
cycle_now = clocksource_read(clock);
|
||||
clock = timekeeper.clock;
|
||||
cycle_now = clock->read(clock);
|
||||
cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;
|
||||
clock->cycle_last = cycle_now;
|
||||
|
||||
nsec = cyc2ns(clock, cycle_delta);
|
||||
nsec = clocksource_cyc2ns(cycle_delta, timekeeper.mult,
|
||||
timekeeper.shift);
|
||||
|
||||
/* If arch requires, add in gettimeoffset() */
|
||||
nsec += arch_gettimeoffset();
|
||||
|
||||
timespec_add_ns(&xtime, nsec);
|
||||
|
||||
nsec = ((s64)cycle_delta * clock->mult_orig) >> clock->shift;
|
||||
clock->raw_time.tv_nsec += nsec;
|
||||
nsec = clocksource_cyc2ns(cycle_delta, clock->mult, clock->shift);
|
||||
timespec_add_ns(&raw_time, nsec);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -95,7 +219,6 @@ static void clocksource_forward_now(void)
|
|||
*/
|
||||
void getnstimeofday(struct timespec *ts)
|
||||
{
|
||||
cycle_t cycle_now, cycle_delta;
|
||||
unsigned long seq;
|
||||
s64 nsecs;
|
||||
|
||||
|
|
@ -105,15 +228,7 @@ void getnstimeofday(struct timespec *ts)
|
|||
seq = read_seqbegin(&xtime_lock);
|
||||
|
||||
*ts = xtime;
|
||||
|
||||
/* read clocksource: */
|
||||
cycle_now = clocksource_read(clock);
|
||||
|
||||
/* calculate the delta since the last update_wall_time: */
|
||||
cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;
|
||||
|
||||
/* convert to nanoseconds: */
|
||||
nsecs = cyc2ns(clock, cycle_delta);
|
||||
nsecs = timekeeping_get_ns();
|
||||
|
||||
/* If arch requires, add in gettimeoffset() */
|
||||
nsecs += arch_gettimeoffset();
|
||||
|
|
@ -125,6 +240,57 @@ void getnstimeofday(struct timespec *ts)
|
|||
|
||||
EXPORT_SYMBOL(getnstimeofday);
|
||||
|
||||
ktime_t ktime_get(void)
|
||||
{
|
||||
unsigned int seq;
|
||||
s64 secs, nsecs;
|
||||
|
||||
WARN_ON(timekeeping_suspended);
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&xtime_lock);
|
||||
secs = xtime.tv_sec + wall_to_monotonic.tv_sec;
|
||||
nsecs = xtime.tv_nsec + wall_to_monotonic.tv_nsec;
|
||||
nsecs += timekeeping_get_ns();
|
||||
|
||||
} while (read_seqretry(&xtime_lock, seq));
|
||||
/*
|
||||
* Use ktime_set/ktime_add_ns to create a proper ktime on
|
||||
* 32-bit architectures without CONFIG_KTIME_SCALAR.
|
||||
*/
|
||||
return ktime_add_ns(ktime_set(secs, 0), nsecs);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ktime_get);
|
||||
|
||||
/**
|
||||
* ktime_get_ts - get the monotonic clock in timespec format
|
||||
* @ts: pointer to timespec variable
|
||||
*
|
||||
* The function calculates the monotonic clock from the realtime
|
||||
* clock and the wall_to_monotonic offset and stores the result
|
||||
* in normalized timespec format in the variable pointed to by @ts.
|
||||
*/
|
||||
void ktime_get_ts(struct timespec *ts)
|
||||
{
|
||||
struct timespec tomono;
|
||||
unsigned int seq;
|
||||
s64 nsecs;
|
||||
|
||||
WARN_ON(timekeeping_suspended);
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&xtime_lock);
|
||||
*ts = xtime;
|
||||
tomono = wall_to_monotonic;
|
||||
nsecs = timekeeping_get_ns();
|
||||
|
||||
} while (read_seqretry(&xtime_lock, seq));
|
||||
|
||||
set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec,
|
||||
ts->tv_nsec + tomono.tv_nsec + nsecs);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ktime_get_ts);
|
||||
|
||||
/**
|
||||
* do_gettimeofday - Returns the time of day in a timeval
|
||||
* @tv: pointer to the timeval to be set
|
||||
|
|
@ -157,7 +323,7 @@ int do_settimeofday(struct timespec *tv)
|
|||
|
||||
write_seqlock_irqsave(&xtime_lock, flags);
|
||||
|
||||
clocksource_forward_now();
|
||||
timekeeping_forward_now();
|
||||
|
||||
ts_delta.tv_sec = tv->tv_sec - xtime.tv_sec;
|
||||
ts_delta.tv_nsec = tv->tv_nsec - xtime.tv_nsec;
|
||||
|
|
@ -167,10 +333,10 @@ int do_settimeofday(struct timespec *tv)
|
|||
|
||||
update_xtime_cache(0);
|
||||
|
||||
clock->error = 0;
|
||||
timekeeper.ntp_error = 0;
|
||||
ntp_clear();
|
||||
|
||||
update_vsyscall(&xtime, clock);
|
||||
update_vsyscall(&xtime, timekeeper.clock);
|
||||
|
||||
write_sequnlock_irqrestore(&xtime_lock, flags);
|
||||
|
||||
|
|
@ -187,44 +353,97 @@ EXPORT_SYMBOL(do_settimeofday);
|
|||
*
|
||||
* Accumulates current time interval and initializes new clocksource
|
||||
*/
|
||||
static void change_clocksource(void)
|
||||
static int change_clocksource(void *data)
|
||||
{
|
||||
struct clocksource *new, *old;
|
||||
|
||||
new = clocksource_get_next();
|
||||
new = (struct clocksource *) data;
|
||||
|
||||
if (clock == new)
|
||||
return;
|
||||
|
||||
clocksource_forward_now();
|
||||
|
||||
if (clocksource_enable(new))
|
||||
return;
|
||||
|
||||
new->raw_time = clock->raw_time;
|
||||
old = clock;
|
||||
clock = new;
|
||||
clocksource_disable(old);
|
||||
|
||||
clock->cycle_last = 0;
|
||||
clock->cycle_last = clocksource_read(clock);
|
||||
clock->error = 0;
|
||||
clock->xtime_nsec = 0;
|
||||
clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH);
|
||||
|
||||
tick_clock_notify();
|
||||
|
||||
/*
|
||||
* We're holding xtime lock and waking up klogd would deadlock
|
||||
* us on enqueue. So no printing!
|
||||
printk(KERN_INFO "Time: %s clocksource has been installed.\n",
|
||||
clock->name);
|
||||
*/
|
||||
timekeeping_forward_now();
|
||||
if (!new->enable || new->enable(new) == 0) {
|
||||
old = timekeeper.clock;
|
||||
timekeeper_setup_internals(new);
|
||||
if (old->disable)
|
||||
old->disable(old);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline void clocksource_forward_now(void) { }
|
||||
static inline void change_clocksource(void) { }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* timekeeping_notify - Install a new clock source
|
||||
* @clock: pointer to the clock source
|
||||
*
|
||||
* This function is called from clocksource.c after a new, better clock
|
||||
* source has been registered. The caller holds the clocksource_mutex.
|
||||
*/
|
||||
void timekeeping_notify(struct clocksource *clock)
|
||||
{
|
||||
if (timekeeper.clock == clock)
|
||||
return;
|
||||
stop_machine(change_clocksource, clock, NULL);
|
||||
tick_clock_notify();
|
||||
}
|
||||
|
||||
#else /* GENERIC_TIME */
|
||||
|
||||
static inline void timekeeping_forward_now(void) { }
|
||||
|
||||
/**
|
||||
* ktime_get - get the monotonic time in ktime_t format
|
||||
*
|
||||
* returns the time in ktime_t format
|
||||
*/
|
||||
ktime_t ktime_get(void)
|
||||
{
|
||||
struct timespec now;
|
||||
|
||||
ktime_get_ts(&now);
|
||||
|
||||
return timespec_to_ktime(now);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ktime_get);
|
||||
|
||||
/**
|
||||
* ktime_get_ts - get the monotonic clock in timespec format
|
||||
* @ts: pointer to timespec variable
|
||||
*
|
||||
* The function calculates the monotonic clock from the realtime
|
||||
* clock and the wall_to_monotonic offset and stores the result
|
||||
* in normalized timespec format in the variable pointed to by @ts.
|
||||
*/
|
||||
void ktime_get_ts(struct timespec *ts)
|
||||
{
|
||||
struct timespec tomono;
|
||||
unsigned long seq;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&xtime_lock);
|
||||
getnstimeofday(ts);
|
||||
tomono = wall_to_monotonic;
|
||||
|
||||
} while (read_seqretry(&xtime_lock, seq));
|
||||
|
||||
set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec,
|
||||
ts->tv_nsec + tomono.tv_nsec);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ktime_get_ts);
|
||||
|
||||
#endif /* !GENERIC_TIME */
|
||||
|
||||
/**
|
||||
* ktime_get_real - get the real (wall-) time in ktime_t format
|
||||
*
|
||||
* returns the time in ktime_t format
|
||||
*/
|
||||
ktime_t ktime_get_real(void)
|
||||
{
|
||||
struct timespec now;
|
||||
|
||||
getnstimeofday(&now);
|
||||
|
||||
return timespec_to_ktime(now);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ktime_get_real);
|
||||
|
||||
/**
|
||||
* getrawmonotonic - Returns the raw monotonic time in a timespec
|
||||
|
|
@ -236,21 +455,11 @@ void getrawmonotonic(struct timespec *ts)
|
|||
{
|
||||
unsigned long seq;
|
||||
s64 nsecs;
|
||||
cycle_t cycle_now, cycle_delta;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&xtime_lock);
|
||||
|
||||
/* read clocksource: */
|
||||
cycle_now = clocksource_read(clock);
|
||||
|
||||
/* calculate the delta since the last update_wall_time: */
|
||||
cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;
|
||||
|
||||
/* convert to nanoseconds: */
|
||||
nsecs = ((s64)cycle_delta * clock->mult_orig) >> clock->shift;
|
||||
|
||||
*ts = clock->raw_time;
|
||||
nsecs = timekeeping_get_ns_raw();
|
||||
*ts = raw_time;
|
||||
|
||||
} while (read_seqretry(&xtime_lock, seq));
|
||||
|
||||
|
|
@ -270,7 +479,7 @@ int timekeeping_valid_for_hres(void)
|
|||
do {
|
||||
seq = read_seqbegin(&xtime_lock);
|
||||
|
||||
ret = clock->flags & CLOCK_SOURCE_VALID_FOR_HRES;
|
||||
ret = timekeeper.clock->flags & CLOCK_SOURCE_VALID_FOR_HRES;
|
||||
|
||||
} while (read_seqretry(&xtime_lock, seq));
|
||||
|
||||
|
|
@ -278,17 +487,33 @@ int timekeeping_valid_for_hres(void)
|
|||
}
|
||||
|
||||
/**
|
||||
* read_persistent_clock - Return time in seconds from the persistent clock.
|
||||
* read_persistent_clock - Return time from the persistent clock.
|
||||
*
|
||||
* Weak dummy function for arches that do not yet support it.
|
||||
* Returns seconds from epoch using the battery backed persistent clock.
|
||||
* Returns zero if unsupported.
|
||||
* Reads the time from the battery backed persistent clock.
|
||||
* Returns a timespec with tv_sec=0 and tv_nsec=0 if unsupported.
|
||||
*
|
||||
* XXX - Do be sure to remove it once all arches implement it.
|
||||
*/
|
||||
unsigned long __attribute__((weak)) read_persistent_clock(void)
|
||||
void __attribute__((weak)) read_persistent_clock(struct timespec *ts)
|
||||
{
|
||||
return 0;
|
||||
ts->tv_sec = 0;
|
||||
ts->tv_nsec = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* read_boot_clock - Return time of the system start.
|
||||
*
|
||||
* Weak dummy function for arches that do not yet support it.
|
||||
* Function to read the exact time the system has been started.
|
||||
* Returns a timespec with tv_sec=0 and tv_nsec=0 if unsupported.
|
||||
*
|
||||
* XXX - Do be sure to remove it once all arches implement it.
|
||||
*/
|
||||
void __attribute__((weak)) read_boot_clock(struct timespec *ts)
|
||||
{
|
||||
ts->tv_sec = 0;
|
||||
ts->tv_nsec = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -296,29 +521,40 @@ unsigned long __attribute__((weak)) read_persistent_clock(void)
|
|||
*/
|
||||
void __init timekeeping_init(void)
|
||||
{
|
||||
struct clocksource *clock;
|
||||
unsigned long flags;
|
||||
unsigned long sec = read_persistent_clock();
|
||||
struct timespec now, boot;
|
||||
|
||||
read_persistent_clock(&now);
|
||||
read_boot_clock(&boot);
|
||||
|
||||
write_seqlock_irqsave(&xtime_lock, flags);
|
||||
|
||||
ntp_init();
|
||||
|
||||
clock = clocksource_get_next();
|
||||
clocksource_enable(clock);
|
||||
clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH);
|
||||
clock->cycle_last = clocksource_read(clock);
|
||||
clock = clocksource_default_clock();
|
||||
if (clock->enable)
|
||||
clock->enable(clock);
|
||||
timekeeper_setup_internals(clock);
|
||||
|
||||
xtime.tv_sec = sec;
|
||||
xtime.tv_nsec = 0;
|
||||
xtime.tv_sec = now.tv_sec;
|
||||
xtime.tv_nsec = now.tv_nsec;
|
||||
raw_time.tv_sec = 0;
|
||||
raw_time.tv_nsec = 0;
|
||||
if (boot.tv_sec == 0 && boot.tv_nsec == 0) {
|
||||
boot.tv_sec = xtime.tv_sec;
|
||||
boot.tv_nsec = xtime.tv_nsec;
|
||||
}
|
||||
set_normalized_timespec(&wall_to_monotonic,
|
||||
-xtime.tv_sec, -xtime.tv_nsec);
|
||||
-boot.tv_sec, -boot.tv_nsec);
|
||||
update_xtime_cache(0);
|
||||
total_sleep_time = 0;
|
||||
total_sleep_time.tv_sec = 0;
|
||||
total_sleep_time.tv_nsec = 0;
|
||||
write_sequnlock_irqrestore(&xtime_lock, flags);
|
||||
}
|
||||
|
||||
/* time in seconds when suspend began */
|
||||
static unsigned long timekeeping_suspend_time;
|
||||
static struct timespec timekeeping_suspend_time;
|
||||
|
||||
/**
|
||||
* timekeeping_resume - Resumes the generic timekeeping subsystem.
|
||||
|
|
@ -331,24 +567,24 @@ static unsigned long timekeeping_suspend_time;
|
|||
static int timekeeping_resume(struct sys_device *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long now = read_persistent_clock();
|
||||
struct timespec ts;
|
||||
|
||||
read_persistent_clock(&ts);
|
||||
|
||||
clocksource_resume();
|
||||
|
||||
write_seqlock_irqsave(&xtime_lock, flags);
|
||||
|
||||
if (now && (now > timekeeping_suspend_time)) {
|
||||
unsigned long sleep_length = now - timekeeping_suspend_time;
|
||||
|
||||
xtime.tv_sec += sleep_length;
|
||||
wall_to_monotonic.tv_sec -= sleep_length;
|
||||
total_sleep_time += sleep_length;
|
||||
if (timespec_compare(&ts, &timekeeping_suspend_time) > 0) {
|
||||
ts = timespec_sub(ts, timekeeping_suspend_time);
|
||||
xtime = timespec_add_safe(xtime, ts);
|
||||
wall_to_monotonic = timespec_sub(wall_to_monotonic, ts);
|
||||
total_sleep_time = timespec_add_safe(total_sleep_time, ts);
|
||||
}
|
||||
update_xtime_cache(0);
|
||||
/* re-base the last cycle value */
|
||||
clock->cycle_last = 0;
|
||||
clock->cycle_last = clocksource_read(clock);
|
||||
clock->error = 0;
|
||||
timekeeper.clock->cycle_last = timekeeper.clock->read(timekeeper.clock);
|
||||
timekeeper.ntp_error = 0;
|
||||
timekeeping_suspended = 0;
|
||||
write_sequnlock_irqrestore(&xtime_lock, flags);
|
||||
|
||||
|
|
@ -366,10 +602,10 @@ static int timekeeping_suspend(struct sys_device *dev, pm_message_t state)
|
|||
{
|
||||
unsigned long flags;
|
||||
|
||||
timekeeping_suspend_time = read_persistent_clock();
|
||||
read_persistent_clock(&timekeeping_suspend_time);
|
||||
|
||||
write_seqlock_irqsave(&xtime_lock, flags);
|
||||
clocksource_forward_now();
|
||||
timekeeping_forward_now();
|
||||
timekeeping_suspended = 1;
|
||||
write_sequnlock_irqrestore(&xtime_lock, flags);
|
||||
|
||||
|
|
@ -404,7 +640,7 @@ device_initcall(timekeeping_init_device);
|
|||
* If the error is already larger, we look ahead even further
|
||||
* to compensate for late or lost adjustments.
|
||||
*/
|
||||
static __always_inline int clocksource_bigadjust(s64 error, s64 *interval,
|
||||
static __always_inline int timekeeping_bigadjust(s64 error, s64 *interval,
|
||||
s64 *offset)
|
||||
{
|
||||
s64 tick_error, i;
|
||||
|
|
@ -420,7 +656,7 @@ static __always_inline int clocksource_bigadjust(s64 error, s64 *interval,
|
|||
* here. This is tuned so that an error of about 1 msec is adjusted
|
||||
* within about 1 sec (or 2^20 nsec in 2^SHIFT_HZ ticks).
|
||||
*/
|
||||
error2 = clock->error >> (NTP_SCALE_SHIFT + 22 - 2 * SHIFT_HZ);
|
||||
error2 = timekeeper.ntp_error >> (NTP_SCALE_SHIFT + 22 - 2 * SHIFT_HZ);
|
||||
error2 = abs(error2);
|
||||
for (look_ahead = 0; error2 > 0; look_ahead++)
|
||||
error2 >>= 2;
|
||||
|
|
@ -429,8 +665,8 @@ static __always_inline int clocksource_bigadjust(s64 error, s64 *interval,
|
|||
* Now calculate the error in (1 << look_ahead) ticks, but first
|
||||
* remove the single look ahead already included in the error.
|
||||
*/
|
||||
tick_error = tick_length >> (NTP_SCALE_SHIFT - clock->shift + 1);
|
||||
tick_error -= clock->xtime_interval >> 1;
|
||||
tick_error = tick_length >> (timekeeper.ntp_error_shift + 1);
|
||||
tick_error -= timekeeper.xtime_interval >> 1;
|
||||
error = ((error - tick_error) >> look_ahead) + tick_error;
|
||||
|
||||
/* Finally calculate the adjustment shift value. */
|
||||
|
|
@ -455,18 +691,18 @@ static __always_inline int clocksource_bigadjust(s64 error, s64 *interval,
|
|||
* this is optimized for the most common adjustments of -1,0,1,
|
||||
* for other values we can do a bit more work.
|
||||
*/
|
||||
static void clocksource_adjust(s64 offset)
|
||||
static void timekeeping_adjust(s64 offset)
|
||||
{
|
||||
s64 error, interval = clock->cycle_interval;
|
||||
s64 error, interval = timekeeper.cycle_interval;
|
||||
int adj;
|
||||
|
||||
error = clock->error >> (NTP_SCALE_SHIFT - clock->shift - 1);
|
||||
error = timekeeper.ntp_error >> (timekeeper.ntp_error_shift - 1);
|
||||
if (error > interval) {
|
||||
error >>= 2;
|
||||
if (likely(error <= interval))
|
||||
adj = 1;
|
||||
else
|
||||
adj = clocksource_bigadjust(error, &interval, &offset);
|
||||
adj = timekeeping_bigadjust(error, &interval, &offset);
|
||||
} else if (error < -interval) {
|
||||
error >>= 2;
|
||||
if (likely(error >= -interval)) {
|
||||
|
|
@ -474,15 +710,15 @@ static void clocksource_adjust(s64 offset)
|
|||
interval = -interval;
|
||||
offset = -offset;
|
||||
} else
|
||||
adj = clocksource_bigadjust(error, &interval, &offset);
|
||||
adj = timekeeping_bigadjust(error, &interval, &offset);
|
||||
} else
|
||||
return;
|
||||
|
||||
clock->mult += adj;
|
||||
clock->xtime_interval += interval;
|
||||
clock->xtime_nsec -= offset;
|
||||
clock->error -= (interval - offset) <<
|
||||
(NTP_SCALE_SHIFT - clock->shift);
|
||||
timekeeper.mult += adj;
|
||||
timekeeper.xtime_interval += interval;
|
||||
timekeeper.xtime_nsec -= offset;
|
||||
timekeeper.ntp_error -= (interval - offset) <<
|
||||
timekeeper.ntp_error_shift;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -492,53 +728,59 @@ static void clocksource_adjust(s64 offset)
|
|||
*/
|
||||
void update_wall_time(void)
|
||||
{
|
||||
struct clocksource *clock;
|
||||
cycle_t offset;
|
||||
u64 nsecs;
|
||||
|
||||
/* Make sure we're fully resumed: */
|
||||
if (unlikely(timekeeping_suspended))
|
||||
return;
|
||||
|
||||
clock = timekeeper.clock;
|
||||
#ifdef CONFIG_GENERIC_TIME
|
||||
offset = (clocksource_read(clock) - clock->cycle_last) & clock->mask;
|
||||
offset = (clock->read(clock) - clock->cycle_last) & clock->mask;
|
||||
#else
|
||||
offset = clock->cycle_interval;
|
||||
offset = timekeeper.cycle_interval;
|
||||
#endif
|
||||
clock->xtime_nsec = (s64)xtime.tv_nsec << clock->shift;
|
||||
timekeeper.xtime_nsec = (s64)xtime.tv_nsec << timekeeper.shift;
|
||||
|
||||
/* normally this loop will run just once, however in the
|
||||
* case of lost or late ticks, it will accumulate correctly.
|
||||
*/
|
||||
while (offset >= clock->cycle_interval) {
|
||||
/* accumulate one interval */
|
||||
offset -= clock->cycle_interval;
|
||||
clock->cycle_last += clock->cycle_interval;
|
||||
while (offset >= timekeeper.cycle_interval) {
|
||||
u64 nsecps = (u64)NSEC_PER_SEC << timekeeper.shift;
|
||||
|
||||
clock->xtime_nsec += clock->xtime_interval;
|
||||
if (clock->xtime_nsec >= (u64)NSEC_PER_SEC << clock->shift) {
|
||||
clock->xtime_nsec -= (u64)NSEC_PER_SEC << clock->shift;
|
||||
/* accumulate one interval */
|
||||
offset -= timekeeper.cycle_interval;
|
||||
clock->cycle_last += timekeeper.cycle_interval;
|
||||
|
||||
timekeeper.xtime_nsec += timekeeper.xtime_interval;
|
||||
if (timekeeper.xtime_nsec >= nsecps) {
|
||||
timekeeper.xtime_nsec -= nsecps;
|
||||
xtime.tv_sec++;
|
||||
second_overflow();
|
||||
}
|
||||
|
||||
clock->raw_time.tv_nsec += clock->raw_interval;
|
||||
if (clock->raw_time.tv_nsec >= NSEC_PER_SEC) {
|
||||
clock->raw_time.tv_nsec -= NSEC_PER_SEC;
|
||||
clock->raw_time.tv_sec++;
|
||||
raw_time.tv_nsec += timekeeper.raw_interval;
|
||||
if (raw_time.tv_nsec >= NSEC_PER_SEC) {
|
||||
raw_time.tv_nsec -= NSEC_PER_SEC;
|
||||
raw_time.tv_sec++;
|
||||
}
|
||||
|
||||
/* accumulate error between NTP and clock interval */
|
||||
clock->error += tick_length;
|
||||
clock->error -= clock->xtime_interval << (NTP_SCALE_SHIFT - clock->shift);
|
||||
timekeeper.ntp_error += tick_length;
|
||||
timekeeper.ntp_error -= timekeeper.xtime_interval <<
|
||||
timekeeper.ntp_error_shift;
|
||||
}
|
||||
|
||||
/* correct the clock when NTP error is too big */
|
||||
clocksource_adjust(offset);
|
||||
timekeeping_adjust(offset);
|
||||
|
||||
/*
|
||||
* Since in the loop above, we accumulate any amount of time
|
||||
* in xtime_nsec over a second into xtime.tv_sec, its possible for
|
||||
* xtime_nsec to be fairly small after the loop. Further, if we're
|
||||
* slightly speeding the clocksource up in clocksource_adjust(),
|
||||
* slightly speeding the clocksource up in timekeeping_adjust(),
|
||||
* its possible the required corrective factor to xtime_nsec could
|
||||
* cause it to underflow.
|
||||
*
|
||||
|
|
@ -550,24 +792,25 @@ void update_wall_time(void)
|
|||
* We'll correct this error next time through this function, when
|
||||
* xtime_nsec is not as small.
|
||||
*/
|
||||
if (unlikely((s64)clock->xtime_nsec < 0)) {
|
||||
s64 neg = -(s64)clock->xtime_nsec;
|
||||
clock->xtime_nsec = 0;
|
||||
clock->error += neg << (NTP_SCALE_SHIFT - clock->shift);
|
||||
if (unlikely((s64)timekeeper.xtime_nsec < 0)) {
|
||||
s64 neg = -(s64)timekeeper.xtime_nsec;
|
||||
timekeeper.xtime_nsec = 0;
|
||||
timekeeper.ntp_error += neg << timekeeper.ntp_error_shift;
|
||||
}
|
||||
|
||||
/* store full nanoseconds into xtime after rounding it up and
|
||||
* add the remainder to the error difference.
|
||||
*/
|
||||
xtime.tv_nsec = ((s64)clock->xtime_nsec >> clock->shift) + 1;
|
||||
clock->xtime_nsec -= (s64)xtime.tv_nsec << clock->shift;
|
||||
clock->error += clock->xtime_nsec << (NTP_SCALE_SHIFT - clock->shift);
|
||||
xtime.tv_nsec = ((s64) timekeeper.xtime_nsec >> timekeeper.shift) + 1;
|
||||
timekeeper.xtime_nsec -= (s64) xtime.tv_nsec << timekeeper.shift;
|
||||
timekeeper.ntp_error += timekeeper.xtime_nsec <<
|
||||
timekeeper.ntp_error_shift;
|
||||
|
||||
update_xtime_cache(cyc2ns(clock, offset));
|
||||
nsecs = clocksource_cyc2ns(offset, timekeeper.mult, timekeeper.shift);
|
||||
update_xtime_cache(nsecs);
|
||||
|
||||
/* check to see if there is a new clocksource to use */
|
||||
change_clocksource();
|
||||
update_vsyscall(&xtime, clock);
|
||||
update_vsyscall(&xtime, timekeeper.clock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -583,9 +826,12 @@ void update_wall_time(void)
|
|||
*/
|
||||
void getboottime(struct timespec *ts)
|
||||
{
|
||||
set_normalized_timespec(ts,
|
||||
- (wall_to_monotonic.tv_sec + total_sleep_time),
|
||||
- wall_to_monotonic.tv_nsec);
|
||||
struct timespec boottime = {
|
||||
.tv_sec = wall_to_monotonic.tv_sec + total_sleep_time.tv_sec,
|
||||
.tv_nsec = wall_to_monotonic.tv_nsec + total_sleep_time.tv_nsec
|
||||
};
|
||||
|
||||
set_normalized_timespec(ts, -boottime.tv_sec, -boottime.tv_nsec);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -594,7 +840,7 @@ void getboottime(struct timespec *ts)
|
|||
*/
|
||||
void monotonic_to_bootbased(struct timespec *ts)
|
||||
{
|
||||
ts->tv_sec += total_sleep_time;
|
||||
*ts = timespec_add_safe(*ts, total_sleep_time);
|
||||
}
|
||||
|
||||
unsigned long get_seconds(void)
|
||||
|
|
@ -603,6 +849,10 @@ unsigned long get_seconds(void)
|
|||
}
|
||||
EXPORT_SYMBOL(get_seconds);
|
||||
|
||||
struct timespec __current_kernel_time(void)
|
||||
{
|
||||
return xtime_cache;
|
||||
}
|
||||
|
||||
struct timespec current_kernel_time(void)
|
||||
{
|
||||
|
|
@ -618,3 +868,20 @@ struct timespec current_kernel_time(void)
|
|||
return now;
|
||||
}
|
||||
EXPORT_SYMBOL(current_kernel_time);
|
||||
|
||||
struct timespec get_monotonic_coarse(void)
|
||||
{
|
||||
struct timespec now, mono;
|
||||
unsigned long seq;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&xtime_lock);
|
||||
|
||||
now = xtime_cache;
|
||||
mono = wall_to_monotonic;
|
||||
} while (read_seqretry(&xtime_lock, seq));
|
||||
|
||||
set_normalized_timespec(&now, now.tv_sec + mono.tv_sec,
|
||||
now.tv_nsec + mono.tv_nsec);
|
||||
return now;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue