Skip to content

Commit 1fb9b7d

Browse files
committed
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 <[email protected]> Signed-off-by: Ingo Molnar <[email protected]>
1 parent 9c17bcd commit 1fb9b7d

File tree

3 files changed

+34
-16
lines changed

3 files changed

+34
-16
lines changed

kernel/time/tick-broadcast.c

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -372,16 +372,8 @@ cpumask_t *tick_get_broadcast_oneshot_mask(void)
372372
static int tick_broadcast_set_event(ktime_t expires, int force)
373373
{
374374
struct clock_event_device *bc = tick_broadcast_device.evtdev;
375-
ktime_t now = ktime_get();
376-
int res;
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-
}
375+
376+
return tick_dev_program_event(bc, expires, force);
385377
}
386378

387379
int tick_resume_broadcast_oneshot(struct clock_event_device *bc)

kernel/time/tick-internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ extern void tick_handle_periodic(struct clock_event_device *dev);
1717
extern void tick_setup_oneshot(struct clock_event_device *newdev,
1818
void (*handler)(struct clock_event_device *),
1919
ktime_t nextevt);
20+
extern int tick_dev_program_event(struct clock_event_device *dev,
21+
ktime_t expires, int force);
2022
extern int tick_program_event(ktime_t expires, int force);
2123
extern void tick_oneshot_notify(void);
2224
extern int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *));

kernel/time/tick-oneshot.c

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,42 @@
2525
/**
2626
* tick_program_event internal worker function
2727
*/
28-
static int __tick_program_event(struct clock_event_device *dev,
29-
ktime_t expires, int force)
28+
int tick_dev_program_event(struct clock_event_device *dev, ktime_t expires,
29+
int force)
3030
{
3131
ktime_t now = ktime_get();
32+
int i;
3233

33-
while (1) {
34+
for (i = 0;;) {
3435
int ret = clockevents_program_event(dev, expires, now);
3536

3637
if (!ret || !force)
3738
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+
3862
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);
4064
}
4165
}
4266

@@ -47,7 +71,7 @@ int tick_program_event(ktime_t expires, int force)
4771
{
4872
struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev;
4973

50-
return __tick_program_event(dev, expires, force);
74+
return tick_dev_program_event(dev, expires, force);
5175
}
5276

5377
/**
@@ -71,7 +95,7 @@ void tick_setup_oneshot(struct clock_event_device *newdev,
7195
{
7296
newdev->event_handler = handler;
7397
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);
7599
}
76100

77101
/**

0 commit comments

Comments
 (0)