mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-13 11:51:32 +00:00
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: Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
parent
ad6759fbf3
commit
80a05b9ffa
@ -73,6 +73,7 @@ enum clock_event_nofitiers {
|
||||
* @list: list head for the management code
|
||||
* @mode: operating mode assigned by the management code
|
||||
* @next_event: local storage for the next event in oneshot mode
|
||||
* @retries: number of forced programming retries
|
||||
*/
|
||||
struct clock_event_device {
|
||||
const char *name;
|
||||
@ -93,6 +94,7 @@ struct clock_event_device {
|
||||
struct list_head list;
|
||||
enum clock_event_mode mode;
|
||||
ktime_t next_event;
|
||||
unsigned long retries;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -22,6 +22,29 @@
|
||||
|
||||
#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
|
||||
*/
|
||||
@ -37,23 +60,28 @@ int tick_dev_program_event(struct clock_event_device *dev, ktime_t expires,
|
||||
if (!ret || !force)
|
||||
return ret;
|
||||
|
||||
dev->retries++;
|
||||
/*
|
||||
* We tried 2 times to program the device with the given
|
||||
* min_delta_ns. If that's not working then we double it
|
||||
* We tried 3 times to program the device with the given
|
||||
* min_delta_ns. If that's not working then we increase it
|
||||
* and emit a warning.
|
||||
*/
|
||||
if (++i > 2) {
|
||||
/* Increase the min. delta and try again */
|
||||
if (!dev->min_delta_ns)
|
||||
dev->min_delta_ns = 5000;
|
||||
else
|
||||
dev->min_delta_ns += dev->min_delta_ns >> 1;
|
||||
|
||||
printk(KERN_WARNING
|
||||
"CE: %s increasing min_delta_ns to %llu nsec\n",
|
||||
dev->name ? dev->name : "?",
|
||||
(unsigned long long) dev->min_delta_ns << 1);
|
||||
|
||||
if (tick_increase_min_delta(dev)) {
|
||||
/*
|
||||
* Get out of the loop if min_delta_ns
|
||||
* hit the limit already. That's
|
||||
* better than staying here forever.
|
||||
*
|
||||
* We clear next_event so we have a
|
||||
* chance that the box survives.
|
||||
*/
|
||||
printk(KERN_WARNING
|
||||
"CE: Reprogramming failure. Giving up\n");
|
||||
dev->next_event.tv64 = KTIME_MAX;
|
||||
return -ETIME;
|
||||
}
|
||||
i = 0;
|
||||
}
|
||||
|
||||
|
@ -228,6 +228,7 @@ print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu)
|
||||
SEQ_printf(m, " event_handler: ");
|
||||
print_name_offset(m, dev->event_handler);
|
||||
SEQ_printf(m, "\n");
|
||||
SEQ_printf(m, " retries: %lu\n", dev->retries);
|
||||
}
|
||||
|
||||
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)
|
||||
u64 now = ktime_to_ns(ktime_get());
|
||||
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, "now at %Ld nsecs\n", (unsigned long long)now);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user