summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorHildner, Christian <christian.hildner@siemens.com>2012-10-08 15:49:03 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-10-21 09:27:59 -0700
commit00dff266efe889d4fc6569f3e39ae7195cb83da7 (patch)
tree27c90c73c8d17598fe841baf0fc3033e3f96307d /kernel
parent3dca360fc8484e40f50205a09f6dbbb591791663 (diff)
downloadlinux-3.10-00dff266efe889d4fc6569f3e39ae7195cb83da7.tar.gz
linux-3.10-00dff266efe889d4fc6569f3e39ae7195cb83da7.tar.bz2
linux-3.10-00dff266efe889d4fc6569f3e39ae7195cb83da7.zip
timers: Fix endless looping between cascade() and internal_add_timer()
commit 26cff4e2aa4d666dc6a120ea34336b5057e3e187 upstream. Adding two (or more) timers with large values for "expires" (they have to reside within tv5 in the same list) leads to endless looping between cascade() and internal_add_timer() in case CONFIG_BASE_SMALL is one and jiffies are crossing the value 1 << 18. The bug was introduced between 2.6.11 and 2.6.12 (and survived for quite some time). This patch ensures that when cascade() is called timers within tv5 are not added endlessly to their own list again, instead they are added to the next lower tv level tv4 (as expected). Signed-off-by: Christian Hildner <christian.hildner@siemens.com> Reviewed-by: Jan Kiszka <jan.kiszka@siemens.com> Link: http://lkml.kernel.org/r/98673C87CB31274881CFFE0B65ECC87B0F5FC1963E@DEFTHW99EA4MSX.ww902.siemens.net Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/timer.c10
1 files changed, 6 insertions, 4 deletions
diff --git a/kernel/timer.c b/kernel/timer.c
index a297ffcf888..6dfdb72828f 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -63,6 +63,7 @@ EXPORT_SYMBOL(jiffies_64);
#define TVR_SIZE (1 << TVR_BITS)
#define TVN_MASK (TVN_SIZE - 1)
#define TVR_MASK (TVR_SIZE - 1)
+#define MAX_TVAL ((unsigned long)((1ULL << (TVR_BITS + 4*TVN_BITS)) - 1))
struct tvec {
struct list_head vec[TVN_SIZE];
@@ -356,11 +357,12 @@ static void internal_add_timer(struct tvec_base *base, struct timer_list *timer)
vec = base->tv1.vec + (base->timer_jiffies & TVR_MASK);
} else {
int i;
- /* If the timeout is larger than 0xffffffff on 64-bit
- * architectures then we use the maximum timeout:
+ /* If the timeout is larger than MAX_TVAL (on 64-bit
+ * architectures or with CONFIG_BASE_SMALL=1) then we
+ * use the maximum timeout.
*/
- if (idx > 0xffffffffUL) {
- idx = 0xffffffffUL;
+ if (idx > MAX_TVAL) {
+ idx = MAX_TVAL;
expires = idx + base->timer_jiffies;
}
i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;