diff options
Diffstat (limited to 'kernel/time/alarmtimer.c')
| -rw-r--r-- | kernel/time/alarmtimer.c | 33 | 
1 files changed, 29 insertions, 4 deletions
diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index 5897828b9d7e..7e5dff602585 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -470,11 +470,35 @@ u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval)  }  EXPORT_SYMBOL_GPL(alarm_forward); -u64 alarm_forward_now(struct alarm *alarm, ktime_t interval) +static u64 __alarm_forward_now(struct alarm *alarm, ktime_t interval, bool throttle)  {  	struct alarm_base *base = &alarm_bases[alarm->type]; +	ktime_t now = base->get_ktime(); + +	if (IS_ENABLED(CONFIG_HIGH_RES_TIMERS) && throttle) { +		/* +		 * Same issue as with posix_timer_fn(). Timers which are +		 * periodic but the signal is ignored can starve the system +		 * with a very small interval. The real fix which was +		 * promised in the context of posix_timer_fn() never +		 * materialized, but someone should really work on it. +		 * +		 * To prevent DOS fake @now to be 1 jiffie out which keeps +		 * the overrun accounting correct but creates an +		 * inconsistency vs. timer_gettime(2). +		 */ +		ktime_t kj = NSEC_PER_SEC / HZ; + +		if (interval < kj) +			now = ktime_add(now, kj); +	} + +	return alarm_forward(alarm, now, interval); +} -	return alarm_forward(alarm, base->get_ktime(), interval); +u64 alarm_forward_now(struct alarm *alarm, ktime_t interval) +{ +	return __alarm_forward_now(alarm, interval, false);  }  EXPORT_SYMBOL_GPL(alarm_forward_now); @@ -551,9 +575,10 @@ static enum alarmtimer_restart alarm_handle_timer(struct alarm *alarm,  	if (posix_timer_event(ptr, si_private) && ptr->it_interval) {  		/*  		 * Handle ignored signals and rearm the timer. This will go -		 * away once we handle ignored signals proper. +		 * away once we handle ignored signals proper. Ensure that +		 * small intervals cannot starve the system.  		 */ -		ptr->it_overrun += alarm_forward_now(alarm, ptr->it_interval); +		ptr->it_overrun += __alarm_forward_now(alarm, ptr->it_interval, true);  		++ptr->it_requeue_pending;  		ptr->it_active = 1;  		result = ALARMTIMER_RESTART;  |