aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/time
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/time')
-rw-r--r--kernel/time/timekeeping.c71
1 files changed, 33 insertions, 38 deletions
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index e91c29f961c9..83d3555a6998 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -58,27 +58,23 @@ struct clocksource *clock;
58 58
59#ifdef CONFIG_GENERIC_TIME 59#ifdef CONFIG_GENERIC_TIME
60/** 60/**
61 * __get_nsec_offset - Returns nanoseconds since last call to periodic_hook 61 * clocksource_forward_now - update clock to the current time
62 * 62 *
63 * private function, must hold xtime_lock lock when being 63 * Forward the current clock to update its state since the last call to
64 * called. Returns the number of nanoseconds since the 64 * update_wall_time(). This is useful before significant clock changes,
65 * last call to update_wall_time() (adjusted by NTP scaling) 65 * as it avoids having to deal with this time offset explicitly.
66 */ 66 */
67static inline s64 __get_nsec_offset(void) 67static void clocksource_forward_now(void)
68{ 68{
69 cycle_t cycle_now, cycle_delta; 69 cycle_t cycle_now, cycle_delta;
70 s64 ns_offset; 70 s64 nsec;
71 71
72 /* read clocksource: */
73 cycle_now = clocksource_read(clock); 72 cycle_now = clocksource_read(clock);
74
75 /* calculate the delta since the last update_wall_time: */
76 cycle_delta = (cycle_now - clock->cycle_last) & clock->mask; 73 cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;
74 clock->cycle_last = cycle_now;
77 75
78 /* convert to nanoseconds: */ 76 nsec = cyc2ns(clock, cycle_delta);
79 ns_offset = cyc2ns(clock, cycle_delta); 77 timespec_add_ns(&xtime, nsec);
80
81 return ns_offset;
82} 78}
83 79
84/** 80/**
@@ -89,6 +85,7 @@ static inline s64 __get_nsec_offset(void)
89 */ 85 */
90void getnstimeofday(struct timespec *ts) 86void getnstimeofday(struct timespec *ts)
91{ 87{
88 cycle_t cycle_now, cycle_delta;
92 unsigned long seq; 89 unsigned long seq;
93 s64 nsecs; 90 s64 nsecs;
94 91
@@ -96,7 +93,15 @@ void getnstimeofday(struct timespec *ts)
96 seq = read_seqbegin(&xtime_lock); 93 seq = read_seqbegin(&xtime_lock);
97 94
98 *ts = xtime; 95 *ts = xtime;
99 nsecs = __get_nsec_offset(); 96
97 /* read clocksource: */
98 cycle_now = clocksource_read(clock);
99
100 /* calculate the delta since the last update_wall_time: */
101 cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;
102
103 /* convert to nanoseconds: */
104 nsecs = cyc2ns(clock, cycle_delta);
100 105
101 } while (read_seqretry(&xtime_lock, seq)); 106 } while (read_seqretry(&xtime_lock, seq));
102 107
@@ -129,22 +134,22 @@ EXPORT_SYMBOL(do_gettimeofday);
129 */ 134 */
130int do_settimeofday(struct timespec *tv) 135int do_settimeofday(struct timespec *tv)
131{ 136{
137 struct timespec ts_delta;
132 unsigned long flags; 138 unsigned long flags;
133 time_t wtm_sec, sec = tv->tv_sec;
134 long wtm_nsec, nsec = tv->tv_nsec;
135 139
136 if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) 140 if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
137 return -EINVAL; 141 return -EINVAL;
138 142
139 write_seqlock_irqsave(&xtime_lock, flags); 143 write_seqlock_irqsave(&xtime_lock, flags);
140 144
141 nsec -= __get_nsec_offset(); 145 clocksource_forward_now();
146
147 ts_delta.tv_sec = tv->tv_sec - xtime.tv_sec;
148 ts_delta.tv_nsec = tv->tv_nsec - xtime.tv_nsec;
149 wall_to_monotonic = timespec_sub(wall_to_monotonic, ts_delta);
142 150
143 wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); 151 xtime = *tv;
144 wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
145 152
146 set_normalized_timespec(&xtime, sec, nsec);
147 set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
148 update_xtime_cache(0); 153 update_xtime_cache(0);
149 154
150 clock->error = 0; 155 clock->error = 0;
@@ -170,22 +175,17 @@ EXPORT_SYMBOL(do_settimeofday);
170static void change_clocksource(void) 175static void change_clocksource(void)
171{ 176{
172 struct clocksource *new; 177 struct clocksource *new;
173 cycle_t now;
174 u64 nsec;
175 178
176 new = clocksource_get_next(); 179 new = clocksource_get_next();
177 180
178 if (clock == new) 181 if (clock == new)
179 return; 182 return;
180 183
181 new->cycle_last = 0; 184 clocksource_forward_now();
182 now = clocksource_read(new);
183 nsec = __get_nsec_offset();
184 timespec_add_ns(&xtime, nsec);
185 185
186 clock = new; 186 clock = new;
187 clock->cycle_last = now; 187 clock->cycle_last = 0;
188 188 clock->cycle_last = clocksource_read(new);
189 clock->error = 0; 189 clock->error = 0;
190 clock->xtime_nsec = 0; 190 clock->xtime_nsec = 0;
191 clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH); 191 clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH);
@@ -200,8 +200,8 @@ static void change_clocksource(void)
200 */ 200 */
201} 201}
202#else 202#else
203static inline void clocksource_forward_now(void) { }
203static inline void change_clocksource(void) { } 204static inline void change_clocksource(void) { }
204static inline s64 __get_nsec_offset(void) { return 0; }
205#endif 205#endif
206 206
207/** 207/**
@@ -265,8 +265,6 @@ void __init timekeeping_init(void)
265static int timekeeping_suspended; 265static int timekeeping_suspended;
266/* time in seconds when suspend began */ 266/* time in seconds when suspend began */
267static unsigned long timekeeping_suspend_time; 267static unsigned long timekeeping_suspend_time;
268/* xtime offset when we went into suspend */
269static s64 timekeeping_suspend_nsecs;
270 268
271/** 269/**
272 * timekeeping_resume - Resumes the generic timekeeping subsystem. 270 * timekeeping_resume - Resumes the generic timekeeping subsystem.
@@ -292,8 +290,6 @@ static int timekeeping_resume(struct sys_device *dev)
292 wall_to_monotonic.tv_sec -= sleep_length; 290 wall_to_monotonic.tv_sec -= sleep_length;
293 total_sleep_time += sleep_length; 291 total_sleep_time += sleep_length;
294 } 292 }
295 /* Make sure that we have the correct xtime reference */
296 timespec_add_ns(&xtime, timekeeping_suspend_nsecs);
297 update_xtime_cache(0); 293 update_xtime_cache(0);
298 /* re-base the last cycle value */ 294 /* re-base the last cycle value */
299 clock->cycle_last = 0; 295 clock->cycle_last = 0;
@@ -319,8 +315,7 @@ static int timekeeping_suspend(struct sys_device *dev, pm_message_t state)
319 timekeeping_suspend_time = read_persistent_clock(); 315 timekeeping_suspend_time = read_persistent_clock();
320 316
321 write_seqlock_irqsave(&xtime_lock, flags); 317 write_seqlock_irqsave(&xtime_lock, flags);
322 /* Get the current xtime offset */ 318 clocksource_forward_now();
323 timekeeping_suspend_nsecs = __get_nsec_offset();
324 timekeeping_suspended = 1; 319 timekeeping_suspended = 1;
325 write_sequnlock_irqrestore(&xtime_lock, flags); 320 write_sequnlock_irqrestore(&xtime_lock, flags);
326 321
@@ -461,10 +456,10 @@ void update_wall_time(void)
461 */ 456 */
462 while (offset >= clock->cycle_interval) { 457 while (offset >= clock->cycle_interval) {
463 /* accumulate one interval */ 458 /* accumulate one interval */
464 clock->xtime_nsec += clock->xtime_interval;
465 clock->cycle_last += clock->cycle_interval;
466 offset -= clock->cycle_interval; 459 offset -= clock->cycle_interval;
460 clock->cycle_last += clock->cycle_interval;
467 461
462 clock->xtime_nsec += clock->xtime_interval;
468 if (clock->xtime_nsec >= (u64)NSEC_PER_SEC << clock->shift) { 463 if (clock->xtime_nsec >= (u64)NSEC_PER_SEC << clock->shift) {
469 clock->xtime_nsec -= (u64)NSEC_PER_SEC << clock->shift; 464 clock->xtime_nsec -= (u64)NSEC_PER_SEC << clock->shift;
470 xtime.tv_sec++; 465 xtime.tv_sec++;