diff options
Diffstat (limited to 'kernel/time/timekeeping.c')
-rw-r--r-- | kernel/time/timekeeping.c | 71 |
1 files changed, 55 insertions, 16 deletions
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index cbc6acb0db3f..9a0bc98fbe1d 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c | |||
@@ -29,6 +29,9 @@ static struct timekeeper timekeeper; | |||
29 | /* flag for if timekeeping is suspended */ | 29 | /* flag for if timekeeping is suspended */ |
30 | int __read_mostly timekeeping_suspended; | 30 | int __read_mostly timekeeping_suspended; |
31 | 31 | ||
32 | /* Flag for if there is a persistent clock on this platform */ | ||
33 | bool __read_mostly persistent_clock_exist = false; | ||
34 | |||
32 | static inline void tk_normalize_xtime(struct timekeeper *tk) | 35 | static inline void tk_normalize_xtime(struct timekeeper *tk) |
33 | { | 36 | { |
34 | while (tk->xtime_nsec >= ((u64)NSEC_PER_SEC << tk->shift)) { | 37 | while (tk->xtime_nsec >= ((u64)NSEC_PER_SEC << tk->shift)) { |
@@ -135,6 +138,20 @@ static void tk_setup_internals(struct timekeeper *tk, struct clocksource *clock) | |||
135 | } | 138 | } |
136 | 139 | ||
137 | /* Timekeeper helper functions. */ | 140 | /* Timekeeper helper functions. */ |
141 | |||
142 | #ifdef CONFIG_ARCH_USES_GETTIMEOFFSET | ||
143 | u32 (*arch_gettimeoffset)(void); | ||
144 | |||
145 | u32 get_arch_timeoffset(void) | ||
146 | { | ||
147 | if (likely(arch_gettimeoffset)) | ||
148 | return arch_gettimeoffset(); | ||
149 | return 0; | ||
150 | } | ||
151 | #else | ||
152 | static inline u32 get_arch_timeoffset(void) { return 0; } | ||
153 | #endif | ||
154 | |||
138 | static inline s64 timekeeping_get_ns(struct timekeeper *tk) | 155 | static inline s64 timekeeping_get_ns(struct timekeeper *tk) |
139 | { | 156 | { |
140 | cycle_t cycle_now, cycle_delta; | 157 | cycle_t cycle_now, cycle_delta; |
@@ -151,8 +168,8 @@ static inline s64 timekeeping_get_ns(struct timekeeper *tk) | |||
151 | nsec = cycle_delta * tk->mult + tk->xtime_nsec; | 168 | nsec = cycle_delta * tk->mult + tk->xtime_nsec; |
152 | nsec >>= tk->shift; | 169 | nsec >>= tk->shift; |
153 | 170 | ||
154 | /* If arch requires, add in gettimeoffset() */ | 171 | /* If arch requires, add in get_arch_timeoffset() */ |
155 | return nsec + arch_gettimeoffset(); | 172 | return nsec + get_arch_timeoffset(); |
156 | } | 173 | } |
157 | 174 | ||
158 | static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk) | 175 | static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk) |
@@ -171,8 +188,8 @@ static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk) | |||
171 | /* convert delta to nanoseconds. */ | 188 | /* convert delta to nanoseconds. */ |
172 | nsec = clocksource_cyc2ns(cycle_delta, clock->mult, clock->shift); | 189 | nsec = clocksource_cyc2ns(cycle_delta, clock->mult, clock->shift); |
173 | 190 | ||
174 | /* If arch requires, add in gettimeoffset() */ | 191 | /* If arch requires, add in get_arch_timeoffset() */ |
175 | return nsec + arch_gettimeoffset(); | 192 | return nsec + get_arch_timeoffset(); |
176 | } | 193 | } |
177 | 194 | ||
178 | static RAW_NOTIFIER_HEAD(pvclock_gtod_chain); | 195 | static RAW_NOTIFIER_HEAD(pvclock_gtod_chain); |
@@ -254,8 +271,8 @@ static void timekeeping_forward_now(struct timekeeper *tk) | |||
254 | 271 | ||
255 | tk->xtime_nsec += cycle_delta * tk->mult; | 272 | tk->xtime_nsec += cycle_delta * tk->mult; |
256 | 273 | ||
257 | /* If arch requires, add in gettimeoffset() */ | 274 | /* If arch requires, add in get_arch_timeoffset() */ |
258 | tk->xtime_nsec += (u64)arch_gettimeoffset() << tk->shift; | 275 | tk->xtime_nsec += (u64)get_arch_timeoffset() << tk->shift; |
259 | 276 | ||
260 | tk_normalize_xtime(tk); | 277 | tk_normalize_xtime(tk); |
261 | 278 | ||
@@ -264,19 +281,18 @@ static void timekeeping_forward_now(struct timekeeper *tk) | |||
264 | } | 281 | } |
265 | 282 | ||
266 | /** | 283 | /** |
267 | * getnstimeofday - Returns the time of day in a timespec | 284 | * __getnstimeofday - Returns the time of day in a timespec. |
268 | * @ts: pointer to the timespec to be set | 285 | * @ts: pointer to the timespec to be set |
269 | * | 286 | * |
270 | * Returns the time of day in a timespec. | 287 | * Updates the time of day in the timespec. |
288 | * Returns 0 on success, or -ve when suspended (timespec will be undefined). | ||
271 | */ | 289 | */ |
272 | void getnstimeofday(struct timespec *ts) | 290 | int __getnstimeofday(struct timespec *ts) |
273 | { | 291 | { |
274 | struct timekeeper *tk = &timekeeper; | 292 | struct timekeeper *tk = &timekeeper; |
275 | unsigned long seq; | 293 | unsigned long seq; |
276 | s64 nsecs = 0; | 294 | s64 nsecs = 0; |
277 | 295 | ||
278 | WARN_ON(timekeeping_suspended); | ||
279 | |||
280 | do { | 296 | do { |
281 | seq = read_seqbegin(&tk->lock); | 297 | seq = read_seqbegin(&tk->lock); |
282 | 298 | ||
@@ -287,6 +303,26 @@ void getnstimeofday(struct timespec *ts) | |||
287 | 303 | ||
288 | ts->tv_nsec = 0; | 304 | ts->tv_nsec = 0; |
289 | timespec_add_ns(ts, nsecs); | 305 | timespec_add_ns(ts, nsecs); |
306 | |||
307 | /* | ||
308 | * Do not bail out early, in case there were callers still using | ||
309 | * the value, even in the face of the WARN_ON. | ||
310 | */ | ||
311 | if (unlikely(timekeeping_suspended)) | ||
312 | return -EAGAIN; | ||
313 | return 0; | ||
314 | } | ||
315 | EXPORT_SYMBOL(__getnstimeofday); | ||
316 | |||
317 | /** | ||
318 | * getnstimeofday - Returns the time of day in a timespec. | ||
319 | * @ts: pointer to the timespec to be set | ||
320 | * | ||
321 | * Returns the time of day in a timespec (WARN if suspended). | ||
322 | */ | ||
323 | void getnstimeofday(struct timespec *ts) | ||
324 | { | ||
325 | WARN_ON(__getnstimeofday(ts)); | ||
290 | } | 326 | } |
291 | EXPORT_SYMBOL(getnstimeofday); | 327 | EXPORT_SYMBOL(getnstimeofday); |
292 | 328 | ||
@@ -640,12 +676,14 @@ void __init timekeeping_init(void) | |||
640 | struct timespec now, boot, tmp; | 676 | struct timespec now, boot, tmp; |
641 | 677 | ||
642 | read_persistent_clock(&now); | 678 | read_persistent_clock(&now); |
679 | |||
643 | if (!timespec_valid_strict(&now)) { | 680 | if (!timespec_valid_strict(&now)) { |
644 | pr_warn("WARNING: Persistent clock returned invalid value!\n" | 681 | pr_warn("WARNING: Persistent clock returned invalid value!\n" |
645 | " Check your CMOS/BIOS settings.\n"); | 682 | " Check your CMOS/BIOS settings.\n"); |
646 | now.tv_sec = 0; | 683 | now.tv_sec = 0; |
647 | now.tv_nsec = 0; | 684 | now.tv_nsec = 0; |
648 | } | 685 | } else if (now.tv_sec || now.tv_nsec) |
686 | persistent_clock_exist = true; | ||
649 | 687 | ||
650 | read_boot_clock(&boot); | 688 | read_boot_clock(&boot); |
651 | if (!timespec_valid_strict(&boot)) { | 689 | if (!timespec_valid_strict(&boot)) { |
@@ -718,11 +756,12 @@ void timekeeping_inject_sleeptime(struct timespec *delta) | |||
718 | { | 756 | { |
719 | struct timekeeper *tk = &timekeeper; | 757 | struct timekeeper *tk = &timekeeper; |
720 | unsigned long flags; | 758 | unsigned long flags; |
721 | struct timespec ts; | ||
722 | 759 | ||
723 | /* Make sure we don't set the clock twice */ | 760 | /* |
724 | read_persistent_clock(&ts); | 761 | * Make sure we don't set the clock twice, as timekeeping_resume() |
725 | if (!(ts.tv_sec == 0 && ts.tv_nsec == 0)) | 762 | * already did it |
763 | */ | ||
764 | if (has_persistent_clock()) | ||
726 | return; | 765 | return; |
727 | 766 | ||
728 | write_seqlock_irqsave(&tk->lock, flags); | 767 | write_seqlock_irqsave(&tk->lock, flags); |