Commit 80a05b9f authored by Thomas Gleixner's avatar Thomas Gleixner

clockevents: Sanitize min_delta_ns adjustment and prevent overflows

The current logic which handles clock events programming failures can
increase min_delta_ns unlimited and even can cause overflows.

Sanitize it by:
 - prevent zero increase when min_delta_ns == 1
 - limiting min_delta_ns to a jiffie
 - bail out if the jiffie limit is hit
 - add retries stats for /proc/timer_list so we can gather data
Reported-by: default avatarUwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent ad6759fb
...@@ -73,6 +73,7 @@ enum clock_event_nofitiers { ...@@ -73,6 +73,7 @@ enum clock_event_nofitiers {
* @list: list head for the management code * @list: list head for the management code
* @mode: operating mode assigned by the management code * @mode: operating mode assigned by the management code
* @next_event: local storage for the next event in oneshot mode * @next_event: local storage for the next event in oneshot mode
* @retries: number of forced programming retries
*/ */
struct clock_event_device { struct clock_event_device {
const char *name; const char *name;
...@@ -93,6 +94,7 @@ struct clock_event_device { ...@@ -93,6 +94,7 @@ struct clock_event_device {
struct list_head list; struct list_head list;
enum clock_event_mode mode; enum clock_event_mode mode;
ktime_t next_event; ktime_t next_event;
unsigned long retries;
}; };
/* /*
......
...@@ -22,6 +22,29 @@ ...@@ -22,6 +22,29 @@
#include "tick-internal.h" #include "tick-internal.h"
/* Limit min_delta to a jiffie */
#define MIN_DELTA_LIMIT (NSEC_PER_SEC / HZ)
static int tick_increase_min_delta(struct clock_event_device *dev)
{
/* Nothing to do if we already reached the limit */
if (dev->min_delta_ns >= MIN_DELTA_LIMIT)
return -ETIME;
if (dev->min_delta_ns < 5000)
dev->min_delta_ns = 5000;
else
dev->min_delta_ns += dev->min_delta_ns >> 1;
if (dev->min_delta_ns > MIN_DELTA_LIMIT)
dev->min_delta_ns = MIN_DELTA_LIMIT;
printk(KERN_WARNING "CE: %s increased min_delta_ns to %llu nsec\n",
dev->name ? dev->name : "?",
(unsigned long long) dev->min_delta_ns);
return 0;
}
/** /**
* tick_program_event internal worker function * tick_program_event internal worker function
*/ */
...@@ -37,23 +60,28 @@ int tick_dev_program_event(struct clock_event_device *dev, ktime_t expires, ...@@ -37,23 +60,28 @@ int tick_dev_program_event(struct clock_event_device *dev, ktime_t expires,
if (!ret || !force) if (!ret || !force)
return ret; return ret;
dev->retries++;
/* /*
* We tried 2 times to program the device with the given * We tried 3 times to program the device with the given
* min_delta_ns. If that's not working then we double it * min_delta_ns. If that's not working then we increase it
* and emit a warning. * and emit a warning.
*/ */
if (++i > 2) { if (++i > 2) {
/* Increase the min. delta and try again */ /* Increase the min. delta and try again */
if (!dev->min_delta_ns) if (tick_increase_min_delta(dev)) {
dev->min_delta_ns = 5000; /*
else * Get out of the loop if min_delta_ns
dev->min_delta_ns += dev->min_delta_ns >> 1; * hit the limit already. That's
* better than staying here forever.
printk(KERN_WARNING *
"CE: %s increasing min_delta_ns to %llu nsec\n", * We clear next_event so we have a
dev->name ? dev->name : "?", * chance that the box survives.
(unsigned long long) dev->min_delta_ns << 1); */
printk(KERN_WARNING
"CE: Reprogramming failure. Giving up\n");
dev->next_event.tv64 = KTIME_MAX;
return -ETIME;
}
i = 0; i = 0;
} }
......
...@@ -228,6 +228,7 @@ print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu) ...@@ -228,6 +228,7 @@ print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu)
SEQ_printf(m, " event_handler: "); SEQ_printf(m, " event_handler: ");
print_name_offset(m, dev->event_handler); print_name_offset(m, dev->event_handler);
SEQ_printf(m, "\n"); SEQ_printf(m, "\n");
SEQ_printf(m, " retries: %lu\n", dev->retries);
} }
static void timer_list_show_tickdevices(struct seq_file *m) static void timer_list_show_tickdevices(struct seq_file *m)
...@@ -257,7 +258,7 @@ static int timer_list_show(struct seq_file *m, void *v) ...@@ -257,7 +258,7 @@ static int timer_list_show(struct seq_file *m, void *v)
u64 now = ktime_to_ns(ktime_get()); u64 now = ktime_to_ns(ktime_get());
int cpu; int cpu;
SEQ_printf(m, "Timer List Version: v0.5\n"); SEQ_printf(m, "Timer List Version: v0.6\n");
SEQ_printf(m, "HRTIMER_MAX_CLOCK_BASES: %d\n", HRTIMER_MAX_CLOCK_BASES); SEQ_printf(m, "HRTIMER_MAX_CLOCK_BASES: %d\n", HRTIMER_MAX_CLOCK_BASES);
SEQ_printf(m, "now at %Ld nsecs\n", (unsigned long long)now); SEQ_printf(m, "now at %Ld nsecs\n", (unsigned long long)now);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment