aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/time
diff options
context:
space:
mode:
authorJohn Stultz <john.stultz@linaro.org>2013-12-11 23:07:49 -0500
committerJohn Stultz <john.stultz@linaro.org>2013-12-23 14:53:26 -0500
commit5258d3f25c76f6ab86e9333abf97a55a877d3870 (patch)
tree1be39352f8537f178ca2eeb2d2802c05edb2be63 /kernel/time
parentf55c07607a38f84b5c7e6066ee1cfe433fa5643c (diff)
timekeeping: Fix potential lost pv notification of time change
In 780427f0e11 (Indicate that clock was set in the pvclock gtod notifier), logic was added to pass a CLOCK_WAS_SET notification to the pvclock notifier chain. While that patch added a action flag returned from accumulate_nsecs_to_secs(), it only uses the returned value in one location, and not in the logarithmic accumulation. This means if a leap second triggered during the logarithmic accumulation (which is most likely where it would happen), the notification that the clock was set would not make it to the pv notifiers. This patch extends the logarithmic_accumulation pass down that action flag so proper notification will occur. This patch also changes the varialbe action -> clock_set per Ingo's suggestion. Cc: Sasha Levin <sasha.levin@oracle.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Ingo Molnar <mingo@kernel.org> Cc: David Vrabel <david.vrabel@citrix.com> Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> Cc: Prarit Bhargava <prarit@redhat.com> Cc: Richard Cochran <richardcochran@gmail.com> Cc: <xen-devel@lists.xen.org> Cc: stable <stable@vger.kernel.org> #3.11+ Signed-off-by: John Stultz <john.stultz@linaro.org>
Diffstat (limited to 'kernel/time')
-rw-r--r--kernel/time/timekeeping.c20
1 files changed, 11 insertions, 9 deletions
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 7488f0b97dee..051855fe68bc 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1256,7 +1256,7 @@ out_adjust:
1256static inline unsigned int accumulate_nsecs_to_secs(struct timekeeper *tk) 1256static inline unsigned int accumulate_nsecs_to_secs(struct timekeeper *tk)
1257{ 1257{
1258 u64 nsecps = (u64)NSEC_PER_SEC << tk->shift; 1258 u64 nsecps = (u64)NSEC_PER_SEC << tk->shift;
1259 unsigned int action = 0; 1259 unsigned int clock_set = 0;
1260 1260
1261 while (tk->xtime_nsec >= nsecps) { 1261 while (tk->xtime_nsec >= nsecps) {
1262 int leap; 1262 int leap;
@@ -1279,10 +1279,10 @@ static inline unsigned int accumulate_nsecs_to_secs(struct timekeeper *tk)
1279 __timekeeping_set_tai_offset(tk, tk->tai_offset - leap); 1279 __timekeeping_set_tai_offset(tk, tk->tai_offset - leap);
1280 1280
1281 clock_was_set_delayed(); 1281 clock_was_set_delayed();
1282 action = TK_CLOCK_WAS_SET; 1282 clock_set = TK_CLOCK_WAS_SET;
1283 } 1283 }
1284 } 1284 }
1285 return action; 1285 return clock_set;
1286} 1286}
1287 1287
1288/** 1288/**
@@ -1295,7 +1295,8 @@ static inline unsigned int accumulate_nsecs_to_secs(struct timekeeper *tk)
1295 * Returns the unconsumed cycles. 1295 * Returns the unconsumed cycles.
1296 */ 1296 */
1297static cycle_t logarithmic_accumulation(struct timekeeper *tk, cycle_t offset, 1297static cycle_t logarithmic_accumulation(struct timekeeper *tk, cycle_t offset,
1298 u32 shift) 1298 u32 shift,
1299 unsigned int *clock_set)
1299{ 1300{
1300 cycle_t interval = tk->cycle_interval << shift; 1301 cycle_t interval = tk->cycle_interval << shift;
1301 u64 raw_nsecs; 1302 u64 raw_nsecs;
@@ -1309,7 +1310,7 @@ static cycle_t logarithmic_accumulation(struct timekeeper *tk, cycle_t offset,
1309 tk->cycle_last += interval; 1310 tk->cycle_last += interval;
1310 1311
1311 tk->xtime_nsec += tk->xtime_interval << shift; 1312 tk->xtime_nsec += tk->xtime_interval << shift;
1312 accumulate_nsecs_to_secs(tk); 1313 *clock_set |= accumulate_nsecs_to_secs(tk);
1313 1314
1314 /* Accumulate raw time */ 1315 /* Accumulate raw time */
1315 raw_nsecs = (u64)tk->raw_interval << shift; 1316 raw_nsecs = (u64)tk->raw_interval << shift;
@@ -1367,7 +1368,7 @@ static void update_wall_time(void)
1367 struct timekeeper *tk = &shadow_timekeeper; 1368 struct timekeeper *tk = &shadow_timekeeper;
1368 cycle_t offset; 1369 cycle_t offset;
1369 int shift = 0, maxshift; 1370 int shift = 0, maxshift;
1370 unsigned int action; 1371 unsigned int clock_set = 0;
1371 unsigned long flags; 1372 unsigned long flags;
1372 1373
1373 raw_spin_lock_irqsave(&timekeeper_lock, flags); 1374 raw_spin_lock_irqsave(&timekeeper_lock, flags);
@@ -1402,7 +1403,8 @@ static void update_wall_time(void)
1402 maxshift = (64 - (ilog2(ntp_tick_length())+1)) - 1; 1403 maxshift = (64 - (ilog2(ntp_tick_length())+1)) - 1;
1403 shift = min(shift, maxshift); 1404 shift = min(shift, maxshift);
1404 while (offset >= tk->cycle_interval) { 1405 while (offset >= tk->cycle_interval) {
1405 offset = logarithmic_accumulation(tk, offset, shift); 1406 offset = logarithmic_accumulation(tk, offset, shift,
1407 &clock_set);
1406 if (offset < tk->cycle_interval<<shift) 1408 if (offset < tk->cycle_interval<<shift)
1407 shift--; 1409 shift--;
1408 } 1410 }
@@ -1420,7 +1422,7 @@ static void update_wall_time(void)
1420 * Finally, make sure that after the rounding 1422 * Finally, make sure that after the rounding
1421 * xtime_nsec isn't larger than NSEC_PER_SEC 1423 * xtime_nsec isn't larger than NSEC_PER_SEC
1422 */ 1424 */
1423 action = accumulate_nsecs_to_secs(tk); 1425 clock_set |= accumulate_nsecs_to_secs(tk);
1424 1426
1425 write_seqcount_begin(&timekeeper_seq); 1427 write_seqcount_begin(&timekeeper_seq);
1426 /* Update clock->cycle_last with the new value */ 1428 /* Update clock->cycle_last with the new value */
@@ -1436,7 +1438,7 @@ static void update_wall_time(void)
1436 * updating. 1438 * updating.
1437 */ 1439 */
1438 memcpy(real_tk, tk, sizeof(*tk)); 1440 memcpy(real_tk, tk, sizeof(*tk));
1439 timekeeping_update(real_tk, action); 1441 timekeeping_update(real_tk, clock_set);
1440 write_seqcount_end(&timekeeper_seq); 1442 write_seqcount_end(&timekeeper_seq);
1441out: 1443out:
1442 raw_spin_unlock_irqrestore(&timekeeper_lock, flags); 1444 raw_spin_unlock_irqrestore(&timekeeper_lock, flags);