aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2008-09-03 17:37:14 -0400
committerThomas Gleixner <tglx@linutronix.de>2008-09-05 05:11:53 -0400
commit1fb9b7d29d8e85ba3196eaa7ab871bf76fc98d36 (patch)
tree1fbefa54e16e76f76616202d8abf9607725afeff /kernel
parent9c17bcda991000351cb2373f78be7e4b1c44caa3 (diff)
clockevents: prevent endless loop lockup
The C1E/HPET bug reports on AMDX2/RS690 systems where tracked down to a too small value of the HPET minumum delta for programming an event. The clockevents code needs to enforce an interrupt event on the clock event device in some cases. The enforcement code was stupid and naive, as it just added the minimum delta to the current time and tried to reprogram the device. When the minimum delta is too small, then this loops forever. Add a sanity check. Allow reprogramming to fail 3 times, then print a warning and double the minimum delta value to make sure, that this does not happen again. Use the same function for both tick-oneshot and tick-broadcast code. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/time/tick-broadcast.c12
-rw-r--r--kernel/time/tick-internal.h2
-rw-r--r--kernel/time/tick-oneshot.c36
3 files changed, 34 insertions, 16 deletions
diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c
index 5744f40b269..2bc1f046151 100644
--- a/kernel/time/tick-broadcast.c
+++ b/kernel/time/tick-broadcast.c
@@ -372,16 +372,8 @@ cpumask_t *tick_get_broadcast_oneshot_mask(void)
372static int tick_broadcast_set_event(ktime_t expires, int force) 372static int tick_broadcast_set_event(ktime_t expires, int force)
373{ 373{
374 struct clock_event_device *bc = tick_broadcast_device.evtdev; 374 struct clock_event_device *bc = tick_broadcast_device.evtdev;
375 ktime_t now = ktime_get(); 375
376 int res; 376 return tick_dev_program_event(bc, expires, force);
377
378 for(;;) {
379 res = clockevents_program_event(bc, expires, now);
380 if (!res || !force)
381 return res;
382 now = ktime_get();
383 expires = ktime_add(now, ktime_set(0, bc->min_delta_ns));
384 }
385} 377}
386 378
387int tick_resume_broadcast_oneshot(struct clock_event_device *bc) 379int tick_resume_broadcast_oneshot(struct clock_event_device *bc)
diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h
index f13f2b7f4fd..0ffc2918ea6 100644
--- a/kernel/time/tick-internal.h
+++ b/kernel/time/tick-internal.h
@@ -17,6 +17,8 @@ extern void tick_handle_periodic(struct clock_event_device *dev);
17extern void tick_setup_oneshot(struct clock_event_device *newdev, 17extern void tick_setup_oneshot(struct clock_event_device *newdev,
18 void (*handler)(struct clock_event_device *), 18 void (*handler)(struct clock_event_device *),
19 ktime_t nextevt); 19 ktime_t nextevt);
20extern int tick_dev_program_event(struct clock_event_device *dev,
21 ktime_t expires, int force);
20extern int tick_program_event(ktime_t expires, int force); 22extern int tick_program_event(ktime_t expires, int force);
21extern void tick_oneshot_notify(void); 23extern void tick_oneshot_notify(void);
22extern int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *)); 24extern int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *));
diff --git a/kernel/time/tick-oneshot.c b/kernel/time/tick-oneshot.c
index 06595c64b0c..2e35501e61d 100644
--- a/kernel/time/tick-oneshot.c
+++ b/kernel/time/tick-oneshot.c
@@ -25,18 +25,42 @@
25/** 25/**
26 * tick_program_event internal worker function 26 * tick_program_event internal worker function
27 */ 27 */
28static int __tick_program_event(struct clock_event_device *dev, 28int tick_dev_program_event(struct clock_event_device *dev, ktime_t expires,
29 ktime_t expires, int force) 29 int force)
30{ 30{
31 ktime_t now = ktime_get(); 31 ktime_t now = ktime_get();
32 int i;
32 33
33 while (1) { 34 for (i = 0;;) {
34 int ret = clockevents_program_event(dev, expires, now); 35 int ret = clockevents_program_event(dev, expires, now);
35 36
36 if (!ret || !force) 37 if (!ret || !force)
37 return ret; 38 return ret;
39
40 /*
41 * We tried 2 times to program the device with the given
42 * min_delta_ns. If that's not working then we double it
43 * and emit a warning.
44 */
45 if (++i > 2) {
46 printk(KERN_WARNING "CE: __tick_program_event of %s is "
47 "stuck %llx %llx\n", dev->name ? dev->name : "?",
48 now.tv64, expires.tv64);
49 printk(KERN_WARNING
50 "CE: increasing min_delta_ns %ld to %ld nsec\n",
51 dev->min_delta_ns, dev->min_delta_ns << 1);
52 WARN_ON(1);
53
54 /* Double the min. delta and try again */
55 if (!dev->min_delta_ns)
56 dev->min_delta_ns = 5000;
57 else
58 dev->min_delta_ns <<= 1;
59 i = 0;
60 }
61
38 now = ktime_get(); 62 now = ktime_get();
39 expires = ktime_add(now, ktime_set(0, dev->min_delta_ns)); 63 expires = ktime_add_ns(now, dev->min_delta_ns);
40 } 64 }
41} 65}
42 66
@@ -47,7 +71,7 @@ int tick_program_event(ktime_t expires, int force)
47{ 71{
48 struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev; 72 struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev;
49 73
50 return __tick_program_event(dev, expires, force); 74 return tick_dev_program_event(dev, expires, force);
51} 75}
52 76
53/** 77/**
@@ -71,7 +95,7 @@ void tick_setup_oneshot(struct clock_event_device *newdev,
71{ 95{
72 newdev->event_handler = handler; 96 newdev->event_handler = handler;
73 clockevents_set_mode(newdev, CLOCK_EVT_MODE_ONESHOT); 97 clockevents_set_mode(newdev, CLOCK_EVT_MODE_ONESHOT);
74 __tick_program_event(newdev, next_event, 1); 98 tick_dev_program_event(newdev, next_event, 1);
75} 99}
76 100
77/** 101/**