aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Stultz <john.stultz@linaro.org>2015-12-03 13:23:30 -0500
committerJohn Stultz <john.stultz@linaro.org>2015-12-16 19:50:57 -0500
commitec02b076ceab63f99e5b3d80fd223d777266c236 (patch)
treede3a12527d72e6063ef99affa2ac1157c756adf4
parentc796348774f15c6e682834ed288bcae0f2c95707 (diff)
timekeeping: Cap adjustments so they don't exceed the maxadj value
Thus its been occasionally noted that users have seen confusing warnings like: Adjusting tsc more than 11% (5941981 vs 7759439) We try to limit the maximum total adjustment to 11% (10% tick adjustment + 0.5% frequency adjustment). But this is done by bounding the requested adjustment values, and the internal steering that is done by tracking the error from what was requested and what was applied, does not have any such limits. This is usually not problematic, but in some cases has a risk that an adjustment could cause the clocksource mult value to overflow, so its an indication things are outside of what is expected. It ends up most of the reports of this 11% warning are on systems using chrony, which utilizes the adjtimex() ADJ_TICK interface (which allows a +-10% adjustment). The original rational for ADJ_TICK unclear to me but my assumption it was originally added to allow broken systems to get a big constant correction at boot (see adjtimex userspace package for an example) which would allow the system to work w/ ntpd's 0.5% adjustment limit. Chrony uses ADJ_TICK to make very aggressive short term corrections (usually right at startup). Which push us close enough to the max bound that a few late ticks can cause the internal steering to push past the max adjust value (tripping the warning). Thus this patch adds some extra logic to enforce the max adjustment cap in the internal steering. Note: This has the potential to slow corrections when the ADJ_TICK value is furthest away from the default value. So it would be good to get some testing from folks using chrony, to make sure we don't cause any troubles there. Cc: Miroslav Lichvar <mlichvar@redhat.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Richard Cochran <richardcochran@gmail.com> Cc: Prarit Bhargava <prarit@redhat.com> Cc: Andy Lutomirski <luto@kernel.org> Tested-by: Miroslav Lichvar <mlichvar@redhat.com> Reported-by: Andy Lutomirski <luto@kernel.org> Signed-off-by: John Stultz <john.stultz@linaro.org>
-rw-r--r--kernel/time/timekeeping.c31
1 files changed, 27 insertions, 4 deletions
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 21cc23918cbd..34b4cedfa80d 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1604,9 +1604,12 @@ static __always_inline void timekeeping_freqadjust(struct timekeeper *tk,
1604{ 1604{
1605 s64 interval = tk->cycle_interval; 1605 s64 interval = tk->cycle_interval;
1606 s64 xinterval = tk->xtime_interval; 1606 s64 xinterval = tk->xtime_interval;
1607 u32 base = tk->tkr_mono.clock->mult;
1608 u32 max = tk->tkr_mono.clock->maxadj;
1609 u32 cur_adj = tk->tkr_mono.mult;
1607 s64 tick_error; 1610 s64 tick_error;
1608 bool negative; 1611 bool negative;
1609 u32 adj; 1612 u32 adj_scale;
1610 1613
1611 /* Remove any current error adj from freq calculation */ 1614 /* Remove any current error adj from freq calculation */
1612 if (tk->ntp_err_mult) 1615 if (tk->ntp_err_mult)
@@ -1625,13 +1628,33 @@ static __always_inline void timekeeping_freqadjust(struct timekeeper *tk,
1625 /* preserve the direction of correction */ 1628 /* preserve the direction of correction */
1626 negative = (tick_error < 0); 1629 negative = (tick_error < 0);
1627 1630
1628 /* Sort out the magnitude of the correction */ 1631 /* If any adjustment would pass the max, just return */
1632 if (negative && (cur_adj - 1) <= (base - max))
1633 return;
1634 if (!negative && (cur_adj + 1) >= (base + max))
1635 return;
1636 /*
1637 * Sort out the magnitude of the correction, but
1638 * avoid making so large a correction that we go
1639 * over the max adjustment.
1640 */
1641 adj_scale = 0;
1629 tick_error = abs(tick_error); 1642 tick_error = abs(tick_error);
1630 for (adj = 0; tick_error > interval; adj++) 1643 while (tick_error > interval) {
1644 u32 adj = 1 << (adj_scale + 1);
1645
1646 /* Check if adjustment gets us within 1 unit from the max */
1647 if (negative && (cur_adj - adj) <= (base - max))
1648 break;
1649 if (!negative && (cur_adj + adj) >= (base + max))
1650 break;
1651
1652 adj_scale++;
1631 tick_error >>= 1; 1653 tick_error >>= 1;
1654 }
1632 1655
1633 /* scale the corrections */ 1656 /* scale the corrections */
1634 timekeeping_apply_adjustment(tk, offset, negative, adj); 1657 timekeeping_apply_adjustment(tk, offset, negative, adj_scale);
1635} 1658}
1636 1659
1637/* 1660/*