aboutsummaryrefslogtreecommitdiff
path: root/init/calibrate.c
diff options
context:
space:
mode:
Diffstat (limited to 'init/calibrate.c')
-rw-r--r--init/calibrate.c22
1 files changed, 18 insertions, 4 deletions
diff --git a/init/calibrate.c b/init/calibrate.c
index f9000dfbe22..76ac9194cbc 100644
--- a/init/calibrate.c
+++ b/init/calibrate.c
@@ -122,7 +122,7 @@ static unsigned long __cpuinit calibrate_delay_direct(void) {return 0;}
static unsigned long __cpuinit calibrate_delay_converge(void)
{
/* First stage - slowly accelerate to find initial bounds */
- unsigned long lpj, ticks, loopadd, chop_limit;
+ unsigned long lpj, lpj_base, ticks, loopadd, loopadd_base, chop_limit;
int trials = 0, band = 0, trial_in_band = 0;
lpj = (1<<12);
@@ -146,14 +146,18 @@ static unsigned long __cpuinit calibrate_delay_converge(void)
* the largest likely undershoot. This defines our chop bounds.
*/
trials -= band;
- loopadd = lpj * band;
- lpj *= trials;
- chop_limit = lpj >> (LPS_PREC + 1);
+ loopadd_base = lpj * band;
+ lpj_base = lpj * trials;
+
+recalibrate:
+ lpj = lpj_base;
+ loopadd = loopadd_base;
/*
* Do a binary approximation to get lpj set to
* equal one clock (up to LPS_PREC bits)
*/
+ chop_limit = lpj >> LPS_PREC;
while (loopadd > chop_limit) {
lpj += loopadd;
ticks = jiffies;
@@ -165,6 +169,16 @@ static unsigned long __cpuinit calibrate_delay_converge(void)
lpj -= loopadd;
loopadd >>= 1;
}
+ /*
+ * If we incremented every single time possible, presume we've
+ * massively underestimated initially, and retry with a higher
+ * start, and larger range. (Only seen on x86_64, due to SMIs)
+ */
+ if (lpj + loopadd * 2 == lpj_base + loopadd_base * 2) {
+ lpj_base = lpj;
+ loopadd_base <<= 2;
+ goto recalibrate;
+ }
return lpj;
}