2002-09-29 19:07:44

by Rolf Fokkens

[permalink] [raw]
Subject: [PATCH] timer.c negative shift, kernel 2.5.39

Hi!

It appears that kernel time keeping may become unpredictable when HZ is
raised to large values due to negative shift operations in timer.c:

time_adj = -ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);

Suppose on x86:
HZ = 2048
and SHIFT_SCALE = 22
and SHIFT_UPDATE = (SHIFT_KG + MAXTC) = 12
then
SHIFT_HZ = 11
and (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE) = -1

This negative number is a problem for shift operations. The
following patch takes care of it (I hope).

Currently include/linux/timex.h prohibits HZ values greater than 1536,
which in turn prevents the case described above... though this is only
because of the values of SHIFT_SCALE, SHIFT_KG and MAXTC. This patch
allows values of HZ up to 3072.


Rolf Fokkens
[email protected]

(3d post of this patch)


diff -ruN linux-2.5.39.orig/kernel/timer.c linux-2.5.39.signedshift/kernel/timer.c
--- linux-2.5.39.orig/kernel/timer.c Sat Sep 28 14:42:56 2002
+++ linux-2.5.39.signedshift/kernel/timer.c Sun Sep 29 18:33:33 2002
@@ -339,6 +339,11 @@
run_task_queue(&tq_immediate);
}

+static inline long signedshift (long val, int nshift)
+{
+ return (nshift > 0 ? val << nshift : val >> -nshift);
+}
+
/*
* this routine handles the overflow of the microsecond field
*
@@ -418,7 +423,7 @@
if (ltemp > (MAXPHASE / MINSEC) << SHIFT_UPDATE)
ltemp = (MAXPHASE / MINSEC) << SHIFT_UPDATE;
time_offset += ltemp;
- time_adj = -ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
+ time_adj = signedshift (-ltemp, SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
} else {
ltemp = time_offset;
if (!(time_status & STA_FLL))
@@ -426,7 +431,7 @@
if (ltemp > (MAXPHASE / MINSEC) << SHIFT_UPDATE)
ltemp = (MAXPHASE / MINSEC) << SHIFT_UPDATE;
time_offset -= ltemp;
- time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
+ time_adj = signedshift (ltemp, SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
}

/*
diff -ruN linux-2.5.39.orig/include/linux/timex.h linux-2.5.39.signedshift/include/linux/timex.h
--- linux-2.5.39.orig/include/linux/timex.h Mon Sep 16 20:43:47 2002
+++ linux-2.5.39.signedshift/include/linux/timex.h Sun Sep 29 18:39:23 2002
@@ -75,6 +75,8 @@
# define SHIFT_HZ 9
#elif HZ >= 768 && HZ < 1536
# define SHIFT_HZ 10
+#elif HZ >= 1536 && HZ < 3072
+# define SHIFT_HZ 11
#else
# error You lose.
#endif